mythix-orm 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/.eslintrc.js +94 -0
  2. package/.vscode/launch.json +34 -0
  3. package/.vscode/settings.json +10 -0
  4. package/LICENSE +21 -0
  5. package/README.md +15 -0
  6. package/lib/connection/connection-base.js +877 -0
  7. package/lib/connection/index.js +11 -0
  8. package/lib/connection/literals/average-literal.js +14 -0
  9. package/lib/connection/literals/count-literal.js +18 -0
  10. package/lib/connection/literals/distinct-literal.js +14 -0
  11. package/lib/connection/literals/index.js +23 -0
  12. package/lib/connection/literals/literal-base.js +62 -0
  13. package/lib/connection/literals/literal-field-base.js +45 -0
  14. package/lib/connection/literals/literal.js +11 -0
  15. package/lib/connection/literals/max-literal.js +14 -0
  16. package/lib/connection/literals/min-literal.js +14 -0
  17. package/lib/connection/literals/sum-literal.js +14 -0
  18. package/lib/connection/query-generator-base.js +864 -0
  19. package/lib/errors/base-error.js +14 -0
  20. package/lib/errors/connection/access-denied-error.js +13 -0
  21. package/lib/errors/connection/connection-acquire-timeout-error.js +13 -0
  22. package/lib/errors/connection/connection-refused-error.js +13 -0
  23. package/lib/errors/connection/connection-timed-out-error.js +13 -0
  24. package/lib/errors/connection/host-not-found-error.js +13 -0
  25. package/lib/errors/connection/host-not-reachable-error.js +13 -0
  26. package/lib/errors/connection/index.js +19 -0
  27. package/lib/errors/connection/invalid-connection-error.js +13 -0
  28. package/lib/errors/connection-base-error.js +13 -0
  29. package/lib/errors/database/exclusion-constraint-error.js +13 -0
  30. package/lib/errors/database/foreign-key-constraint-error.js +13 -0
  31. package/lib/errors/database/index.js +13 -0
  32. package/lib/errors/database/timeout-error.js +13 -0
  33. package/lib/errors/database/unknown-constraint-error.js +13 -0
  34. package/lib/errors/database-base-error.js +17 -0
  35. package/lib/errors/index.js +44 -0
  36. package/lib/field.js +18 -0
  37. package/lib/index.js +43 -0
  38. package/lib/model.js +1374 -0
  39. package/lib/proxy-class/index.js +3 -0
  40. package/lib/proxy-class/proxy-class.js +269 -0
  41. package/lib/query-engine/field-scope.js +99 -0
  42. package/lib/query-engine/index.js +13 -0
  43. package/lib/query-engine/model-scope.js +198 -0
  44. package/lib/query-engine/query-engine-base.js +325 -0
  45. package/lib/query-engine/query-engine.js +212 -0
  46. package/lib/types/concrete/bigint-type.js +62 -0
  47. package/lib/types/concrete/blob-type.js +46 -0
  48. package/lib/types/concrete/boolean-type.js +41 -0
  49. package/lib/types/concrete/char-type.js +39 -0
  50. package/lib/types/concrete/date-type.js +87 -0
  51. package/lib/types/concrete/datetime-type.js +92 -0
  52. package/lib/types/concrete/float-type.js +47 -0
  53. package/lib/types/concrete/foreign-key-type.js +208 -0
  54. package/lib/types/concrete/index.js +53 -0
  55. package/lib/types/concrete/integer-type.js +51 -0
  56. package/lib/types/concrete/string-type.js +44 -0
  57. package/lib/types/concrete/text-type.js +44 -0
  58. package/lib/types/concrete/uuid-base.js +77 -0
  59. package/lib/types/concrete/uuid-v1-type.js +99 -0
  60. package/lib/types/concrete/uuid-v3-type.js +108 -0
  61. package/lib/types/concrete/uuid-v4-type.js +90 -0
  62. package/lib/types/concrete/uuid-v5-type.js +108 -0
  63. package/lib/types/concrete/xid-type.js +58 -0
  64. package/lib/types/helpers/default-helpers.js +127 -0
  65. package/lib/types/helpers/index.js +35 -0
  66. package/lib/types/index.js +94 -0
  67. package/lib/types/type.js +244 -0
  68. package/lib/types/virtual/index.js +14 -0
  69. package/lib/types/virtual/model-type.js +141 -0
  70. package/lib/types/virtual/models-type.js +264 -0
  71. package/lib/types/virtual/relational-type-base.js +323 -0
  72. package/lib/utils/index.js +65 -0
  73. package/lib/utils/misc-utils.js +73 -0
  74. package/lib/utils/model-utils.js +704 -0
  75. package/lib/utils/query-utils.js +186 -0
  76. package/package.json +63 -0
  77. package/playground/test01.js +15 -0
  78. package/spec/connection/connection-base-spec.js +126 -0
  79. package/spec/connection/literals/average-literal-spec.js +45 -0
  80. package/spec/connection/literals/count-literal-spec.js +42 -0
  81. package/spec/connection/literals/distinct-literal-spec.js +42 -0
  82. package/spec/connection/literals/literal-spec.js +26 -0
  83. package/spec/connection/literals/max-literal-spec.js +42 -0
  84. package/spec/connection/literals/min-literal-spec.js +42 -0
  85. package/spec/connection/literals/sum-literal-spec.js +42 -0
  86. package/spec/helpers/default-helpers-spec.js +108 -0
  87. package/spec/model-spec.js +736 -0
  88. package/spec/proxy-class/proxy-class-spec.js +173 -0
  89. package/spec/query-engine/__snapshots__/QueryEngine-operations-query_operations_and_chaining-can_chain_query_conditions-001.snapshot +94 -0
  90. package/spec/query-engine/__snapshots__/QueryEngine-operations-query_operations_and_chaining-can_construct_a_query_context_with_a_model_call-001.snapshot +35 -0
  91. package/spec/query-engine/__snapshots__/QueryEngine-operations-query_operations_and_chaining-can_construct_a_query_context_with_a_model_sub-001.snapshot +35 -0
  92. package/spec/query-engine/__snapshots__/QueryEngine-operations-query_operations_and_chaining-can_set_a_default_scope_on_a_model-001.snapshot +57 -0
  93. package/spec/query-engine/__snapshots__/QueryEngine-operations-query_operations_and_chaining-can_unscope_default_scope_on_a_model-001.snapshot +35 -0
  94. package/spec/query-engine/query-engine-spec.js +99 -0
  95. package/spec/support/jasmine.json +13 -0
  96. package/spec/support/models/blob-test-model.js +19 -0
  97. package/spec/support/models/extended-user-model.js +38 -0
  98. package/spec/support/models/index.js +27 -0
  99. package/spec/support/models/number-model.js +24 -0
  100. package/spec/support/models/role-model.js +26 -0
  101. package/spec/support/models/role-thing-model.js +41 -0
  102. package/spec/support/models/scoped-user-model.js +13 -0
  103. package/spec/support/models/time-model.js +36 -0
  104. package/spec/support/models/user-model.js +70 -0
  105. package/spec/support/models/user-role-model.js +36 -0
  106. package/spec/support/models/user-thing-model.js +46 -0
  107. package/spec/support/models/validation-test-model.js +40 -0
  108. package/spec/support/snapshots.js +293 -0
  109. package/spec/support/test-helpers.js +13 -0
  110. package/spec/types/concrete/bigint-type-spec.js +84 -0
  111. package/spec/types/concrete/boolean-type-spec.js +83 -0
  112. package/spec/types/concrete/date-type-spec.js +85 -0
  113. package/spec/types/concrete/datetime-type-spec.js +87 -0
  114. package/spec/types/concrete/float-type-spec.js +71 -0
  115. package/spec/types/concrete/foreign-key-type-spec.js +64 -0
  116. package/spec/types/concrete/integer-type-spec.js +71 -0
  117. package/spec/types/concrete/string-type-spec.js +91 -0
  118. package/spec/types/concrete/uuid-v1-type-spec.js +73 -0
  119. package/spec/types/concrete/uuid-v4-type-spec.js +65 -0
  120. package/spec/types/type-spec.js +101 -0
  121. package/spec/types/virtual/model-types-spec.js +401 -0
  122. package/spec/utils/misc-utils-spec.js +61 -0
  123. package/spec/utils/model-utils-spec.js +55 -0
  124. package/spec/utils/query-utils-spec.js +105 -0
@@ -0,0 +1,186 @@
1
+ 'use strict';
2
+
3
+ const Nife = require('nife');
4
+
5
+ // The code below will take a "query object"
6
+ // and convert it into Mythix ORM query.
7
+ //
8
+ // "query objects" are objects with a simple
9
+ // structure and convention to build complex queries.
10
+ //
11
+ // Fields inside these objects can have operators,
12
+ // which are postfixed to the field name. For example,
13
+ // you could create a filter to find a user by name
14
+ // with the following query object:
15
+ // { "firstName=": "John", "lastName!=": "Bob" }
16
+ // which would find all users with the first name
17
+ // of "John", and any last name except "Bob".
18
+ //
19
+ // AND and OR conditions are also supported. These
20
+ // work based of the structure of the object itself.
21
+ // If an array is used, then OR is in effect.
22
+ // If an object is used, then AND is in effect.
23
+ // For example, the following query object:
24
+ // [ { firstName: "John", lastName: "Brown" }, { firstName: "Mary", lastName: "Smith" } ]
25
+ // would result in the following query:
26
+ // WHERE ((firstName = 'John' AND lastName = 'Brown') OR (firstName = 'Mary' AND lastName = 'Smith')),
27
+ // finding either user John Brown, or Mary Smith.
28
+ //
29
+ // IN and NOT IN operators are handled automatically
30
+ // when the operator is either "=" or "!=", and the
31
+ // provided value is an array. For example:
32
+ // { firstName: [ 'John', 'Bob', 'Mary' ] }
33
+ // would result in the following query:
34
+ // WHERE firstName IN ('John', 'Bob', 'Mary')
35
+
36
+ const FILTER_OPERATORS = {
37
+ '=': (Model, fieldName, query, value) => {
38
+ return query.AND[fieldName].EQ(value);
39
+ },
40
+ '!=': (Model, fieldName, query, value) => {
41
+ return query.AND[fieldName].NEQ(value);
42
+ },
43
+ '>': (Model, fieldName, query, value) => {
44
+ return query.AND[fieldName].GT(value);
45
+ },
46
+ '>=': (Model, fieldName, query, value) => {
47
+ return query.AND[fieldName].GTE(value);
48
+ },
49
+ '<': (Model, fieldName, query, value) => {
50
+ return query.AND[fieldName].LT(value);
51
+ },
52
+ '<=': (Model, fieldName, query, value) => {
53
+ return query.AND[fieldName].LTE(value);
54
+ },
55
+ // Between
56
+ '><': (Model, fieldName, query, value) => {
57
+ return query.AND(Model.where[fieldName].GTE(value[0]).AND[fieldName].LTE(value[1]));
58
+ },
59
+ // Not between
60
+ '<>': (Model, fieldName, query, value) => {
61
+ return query.AND(Model.where[fieldName].LT(value[0]).OR[fieldName].GT(value[1]));
62
+ },
63
+ // Like (TODO: Mythix ORM needs to support this)
64
+ '*': (Model, fieldName, query, value) => {
65
+ return query.EQ(value);
66
+ },
67
+ // NOT Like (TODO: Mythix ORM needs to support this)
68
+ '!*': (Model, fieldName, query, value) => {
69
+ return query.EQ(value);
70
+ },
71
+ };
72
+
73
+ function parseFilterFieldAndOperator(fieldName) {
74
+ let operator = '=';
75
+ let field;
76
+
77
+ ('' + fieldName).replace(/^\s*(\w+)\s*(.*)$/, (m, _field, _operator) => {
78
+ field = _field.trim();
79
+
80
+ if (Nife.isNotEmpty(_operator))
81
+ operator = _operator.trim();
82
+ });
83
+
84
+ if (Nife.isEmpty(field))
85
+ throw new Error('generateQueryFromFilter: "field" is blank');
86
+
87
+ if (!Object.prototype.hasOwnProperty.call(FILTER_OPERATORS, operator))
88
+ throw new Error(`generateQueryFromFilter: Unknown operator "${operator}"`);
89
+
90
+ return { field, operator };
91
+ }
92
+
93
+ function generateQueryFromFilter(connection, Model, _filter, _depth) {
94
+ const getOperator = (name) => {
95
+ let func = FILTER_OPERATORS[name];
96
+ if (!func)
97
+ throw new Error(`generateQueryFromFilter: No such operator "${name}"`);
98
+
99
+ return func;
100
+ };
101
+
102
+ let filter = _filter;
103
+ let query = Model.where(connection);
104
+ let depth = _depth || 0;
105
+
106
+ // [ { id: 1 }, { name: 'derp' }, [ { 'dob>=': '2000-01-01', 'role': [ 'person', 'dog' ] } ] ]...
107
+ // SQL = `id = 1 OR name = 'derp' OR (dob >= '2000-01-01' AND role IN [ 'person', 'dog' ])`
108
+
109
+ // If filter provided is a mythix model,
110
+ // then convert the model attributes to
111
+ // an object so we can iterate the attributes
112
+ if (!Nife.instanceOf(filter, 'array', 'object', 'set', 'map')) {
113
+ if (Model.isModel(filter)) {
114
+ let pkFieldName = filter.getPrimaryKeyFieldName();
115
+ let primaryKeyValue = filter[pkFieldName];
116
+
117
+ if (pkFieldName && primaryKeyValue != null)
118
+ filter = { [pkFieldName]: primaryKeyValue };
119
+ else
120
+ filter = filter.toJSON();
121
+ } else {
122
+ return;
123
+ }
124
+ }
125
+
126
+ Nife.iterate(filter, ({ key, value: _value, type }) => {
127
+ let isArrayType = (type === 'Array' || type === 'Set');
128
+ let fieldName = (isArrayType) ? null : key;
129
+ let value = _value;
130
+
131
+ if (isArrayType && value == null)
132
+ return;
133
+
134
+ if (value === undefined)
135
+ return;
136
+
137
+ if (isArrayType && (Nife.instanceOf(value, 'array', 'object', 'set', 'map') || Model.isModel(value))) {
138
+ query = query.OR(generateQueryFromFilter(connection, Model, value, depth + 1));
139
+ } else {
140
+ let {
141
+ field,
142
+ operator,
143
+ } = parseFilterFieldAndOperator(fieldName);
144
+
145
+ if (!field)
146
+ return;
147
+
148
+ let modelField = Model.getField(field);
149
+ if (!modelField || modelField.type.isVirtual())
150
+ return;
151
+
152
+ let queryOperator = getOperator(operator);
153
+
154
+ if (operator !== '<>' && operator !== '><' && Nife.instanceOf(value, 'array', 'set')) {
155
+ if (Nife.instanceOf(value, 'set'))
156
+ value = Array.from(value.values());
157
+
158
+ value = value.filter((v) => Nife.isNotEmpty(v));
159
+
160
+ if (Nife.isEmpty(value))
161
+ return;
162
+
163
+ if (operator === '=' || operator === '!=')
164
+ queryOperator = getOperator(operator);
165
+ else
166
+ throw new Error(`Invalid array value for operator "${operator}"`);
167
+ }
168
+
169
+ if (value === null) {
170
+ if (operator === '=' || operator === '!=')
171
+ queryOperator = getOperator(operator);
172
+ else
173
+ throw new Error(`Invalid "NULL" value for operator "${operator}"`);
174
+ }
175
+
176
+ query = queryOperator(Model, field, query, value);
177
+ }
178
+ });
179
+
180
+ return query;
181
+ }
182
+
183
+ module.exports = {
184
+ parseFilterFieldAndOperator,
185
+ generateQueryFromFilter,
186
+ };
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "mythix-orm",
3
+ "version": "1.0.1",
4
+ "description": "ORM for Mythix framework",
5
+ "main": "lib/index.js",
6
+ "type": "commonjs",
7
+ "scripts": {
8
+ "coverage": "clear ; node ./node_modules/.bin/nyc ./node_modules/.bin/jasmine",
9
+ "test": "node ./node_modules/.bin/jasmine",
10
+ "test-debug": "node --inspect-brk ./node_modules/.bin/jasmine",
11
+ "test-watch": "watch 'clear ; node ./node_modules/.bin/jasmine' . --wait=2 --interval=1"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/th317erd/mythix-orm.git"
16
+ },
17
+ "keywords": [
18
+ "orm",
19
+ "mysql",
20
+ "postgres",
21
+ "postgresql",
22
+ "mssql",
23
+ "mongo",
24
+ "snowflake",
25
+ "database",
26
+ "sql",
27
+ "no-sql"
28
+ ],
29
+ "author": "Wyatt Greenway",
30
+ "license": "MIT",
31
+ "bugs": {
32
+ "url": "https://github.com/th317erd/mythix-orm/issues"
33
+ },
34
+ "homepage": "https://github.com/th317erd/mythix-orm#readme",
35
+ "devDependencies": {
36
+ "@spothero/eslint-plugin-spothero": "github:spothero/eslint-plugin-spothero",
37
+ "better-sqlite3": "^7.6.2",
38
+ "colors": "^1.4.0",
39
+ "diff": "^5.1.0",
40
+ "eslint": "^8.13.0",
41
+ "jasmine": "^4.1.0",
42
+ "nyc": "^15.1.0"
43
+ },
44
+ "dependencies": {
45
+ "events": "^3.3.0",
46
+ "inflection": "^1.13.2",
47
+ "moment": "^2.29.3",
48
+ "nife": "^1.11.2",
49
+ "sqlstring": "^2.3.3",
50
+ "uuid": "^8.3.2",
51
+ "xid-js": "^1.0.1"
52
+ },
53
+ "nyc": {
54
+ "reporter": [
55
+ "text",
56
+ "html"
57
+ ],
58
+ "exclude": [
59
+ "spec/**",
60
+ "lib/proxy-class/proxy-class.js"
61
+ ]
62
+ }
63
+ }
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ class _Test {
4
+
5
+ }
6
+
7
+ const Test = new Proxy(_Test, {
8
+ set(obj, prop, value) {
9
+ console.log('Setting: ', obj, prop, value);
10
+ },
11
+ });
12
+
13
+ class Test2 extends Test {
14
+ fields = { derp: true };
15
+ }
@@ -0,0 +1,126 @@
1
+ /* eslint-disable no-magic-numbers */
2
+
3
+ 'use strict';
4
+
5
+ /* global describe, it, expect, beforeAll, fail, spyOn */
6
+
7
+ const { Model, ConnectionBase, Types } = require('../../lib');
8
+ const { User } = require('../support/models');
9
+
10
+ class TestModel extends Model {
11
+ static fields = {
12
+ 'id': {
13
+ type: Types.UUIDV4,
14
+ defaultValue: Types.UUIDV4.Default.UUIDV4,
15
+ allowNull: false,
16
+ primaryKey: true,
17
+ },
18
+ 'firstName': {
19
+ type: Types.STRING(64),
20
+ allowNull: true,
21
+ index: true,
22
+ },
23
+ 'lastName': {
24
+ type: Types.STRING(64),
25
+ allowNull: true,
26
+ index: true,
27
+ },
28
+ };
29
+ }
30
+
31
+ describe('ConnectionBase', () => {
32
+ let connection;
33
+
34
+ beforeAll(() => {
35
+ try {
36
+ connection = new ConnectionBase({ models: [ TestModel ] });
37
+ } catch (error) {
38
+ console.error('Error in BeforeAll: ', error);
39
+ }
40
+ });
41
+
42
+ describe('Model', () => {
43
+ describe('where', () => {
44
+ it('should be able to get a query engine from where', () => {
45
+ expect(User.where(connection)._getRawQuery()[0].rootModelName).toEqual('User');
46
+ expect(User.where(connection)._getRawQuery()[0].Model.getModelName()).toEqual('User');
47
+ });
48
+ });
49
+
50
+ describe('getUnscopedQueryEngine', () => {
51
+ it('should be able to get a query engine from getUnscopedQueryEngine', () => {
52
+ let user = new TestModel();
53
+
54
+ expect(user.getUnscopedQueryEngine()._getRawQuery()[0].rootModelName).toEqual('TestModel');
55
+ expect(user.getUnscopedQueryEngine()._getRawQuery()[0].Model.getModelName()).toEqual('TestModel');
56
+ });
57
+ });
58
+
59
+ describe('getQueryEngine', () => {
60
+ it('should be able to get a query engine from getQueryEngine', () => {
61
+ let user = new TestModel();
62
+
63
+ expect(user.getQueryEngine()._getRawQuery()[0].rootModelName).toEqual('TestModel');
64
+ expect(user.getQueryEngine()._getRawQuery()[0].Model.getModelName()).toEqual('TestModel');
65
+ });
66
+ });
67
+ });
68
+
69
+ describe('getModel', () => {
70
+ it('should be able to get a model by name', () => {
71
+ let model = connection.getModel('TestModel');
72
+ expect(model.getModelName()).toEqual('TestModel');
73
+ });
74
+
75
+ it('should be able to get a model by a fully qualified name', () => {
76
+ let model = connection.getModel('TestModel:id');
77
+ expect(model.getModelName()).toEqual('TestModel');
78
+
79
+ model = connection.getModel('TestModel::id.field');
80
+ expect(model.getModelName()).toEqual('TestModel');
81
+
82
+ model = connection.getModel('TestModel::');
83
+ expect(model.getModelName()).toEqual('TestModel');
84
+ });
85
+ });
86
+
87
+ describe('getField', () => {
88
+ it('should fail if model not defined', () => {
89
+ let field = connection.getField('firstName');
90
+ expect(field).toBe(undefined);
91
+ });
92
+
93
+ it('should be able to get a field by name', () => {
94
+ let field = connection.getField('firstName', 'TestModel');
95
+ expect(field.fieldName).toEqual('firstName');
96
+ expect(field.Model.getModelName()).toEqual('TestModel');
97
+ });
98
+
99
+ it('should be able to get a field by a fully qualified name', () => {
100
+ let field = connection.getField('TestModel:firstName');
101
+ expect(field.fieldName).toEqual('firstName');
102
+ expect(field.Model.getModelName()).toEqual('TestModel');
103
+ });
104
+ });
105
+
106
+ describe('start', () => {
107
+ it('should throw an unimplemented error', async () => {
108
+ try {
109
+ await connection.start();
110
+ fail('unreachable');
111
+ } catch (error) {
112
+ expect(error).toBeInstanceOf(Error);
113
+ expect(error.message).toEqual('ConnectionBase::start: Child class is required to implement "start".');
114
+ }
115
+ });
116
+ });
117
+
118
+ describe('stop', () => {
119
+ it('should unbind all event listeners', async () => {
120
+ spyOn(connection, 'removeAllListeners').and.callThrough();
121
+ await connection.stop();
122
+
123
+ expect(connection.removeAllListeners.calls.count()).toEqual(1);
124
+ });
125
+ });
126
+ });
@@ -0,0 +1,45 @@
1
+ /* eslint-disable no-magic-numbers */
2
+
3
+ 'use strict';
4
+
5
+ /* global describe, it, expect, beforeAll */
6
+
7
+ const ConnectionBase = require('../../../lib/connection/connection-base');
8
+ const { AverageLiteral, Literal } = require('../../../lib/connection/literals');
9
+
10
+ describe('AverageLiteral', () => {
11
+ let connection;
12
+ let User;
13
+
14
+ beforeAll(async () => {
15
+ try {
16
+ connection = new ConnectionBase({
17
+ bindModels: false,
18
+ models: require('../../support/models'),
19
+ });
20
+
21
+ let models = connection.getModels();
22
+ User = models.User;
23
+ } catch (error) {
24
+ console.error('Error in beforeAll: ', error);
25
+ }
26
+ });
27
+
28
+ describe('toString', () => {
29
+ it('can turn a fully qualified name into a min projection', () => {
30
+ expect((new AverageLiteral('User:id')).toString(connection)).toEqual('AVG("users"."id")');
31
+ });
32
+
33
+ it('will throw an exception if no field is present', () => {
34
+ expect(() => (new AverageLiteral()).toString(connection)).toThrow(new TypeError('AverageLiteral::fullyQualifiedNameToDefinition: Unable to find field for fully qualified name "undefined".'));
35
+ });
36
+
37
+ it('can turn a raw field into a projection field', () => {
38
+ expect((new AverageLiteral(User.fields.firstName)).toString(connection)).toEqual('AVG("users"."firstName")');
39
+ });
40
+
41
+ it('can provide a SQL literal', () => {
42
+ expect((new AverageLiteral(new Literal('test'))).toString(connection)).toEqual('AVG(test)');
43
+ });
44
+ });
45
+ });
@@ -0,0 +1,42 @@
1
+ /* eslint-disable no-magic-numbers */
2
+
3
+ 'use strict';
4
+
5
+ /* global describe, it, expect, beforeAll */
6
+
7
+ const ConnectionBase = require('../../../lib/connection/connection-base');
8
+ const { CountLiteral, Literal } = require('../../../lib/connection/literals');
9
+
10
+ describe('CountLiteral', () => {
11
+ let connection;
12
+ let User;
13
+
14
+ beforeAll(async () => {
15
+ connection = new ConnectionBase({
16
+ bindModels: false,
17
+ models: require('../../support/models'),
18
+ });
19
+
20
+ let models = connection.getModels();
21
+
22
+ User = models.User;
23
+ });
24
+
25
+ describe('toString', () => {
26
+ it('can turn a fully qualified name into a count projection', () => {
27
+ expect((new CountLiteral('User:id')).toString(connection)).toEqual('COUNT("users"."id")');
28
+ });
29
+
30
+ it('will default to star if no field is present', () => {
31
+ expect((new CountLiteral()).toString(connection)).toEqual('COUNT(*)');
32
+ });
33
+
34
+ it('can turn a raw field into a projection field', () => {
35
+ expect((new CountLiteral(User.fields.firstName)).toString(connection)).toEqual('COUNT("users"."firstName")');
36
+ });
37
+
38
+ it('can provide a SQL literal', () => {
39
+ expect((new CountLiteral(new Literal('test'))).toString(connection)).toEqual('COUNT(test)');
40
+ });
41
+ });
42
+ });
@@ -0,0 +1,42 @@
1
+ /* eslint-disable no-magic-numbers */
2
+
3
+ 'use strict';
4
+
5
+ /* global describe, it, expect, beforeAll */
6
+
7
+ const ConnectionBase = require('../../../lib/connection/connection-base');
8
+ const { DistinctLiteral, Literal } = require('../../../lib/connection/literals');
9
+
10
+ describe('DistinctLiteral', () => {
11
+ let connection;
12
+ let User;
13
+
14
+ beforeAll(async () => {
15
+ connection = new ConnectionBase({
16
+ bindModels: false,
17
+ models: require('../../support/models'),
18
+ });
19
+
20
+ let models = connection.getModels();
21
+
22
+ User = models.User;
23
+ });
24
+
25
+ describe('toString', () => {
26
+ it('can turn a fully qualified name into a projection field', () => {
27
+ expect((new DistinctLiteral('User:id')).toString(connection)).toEqual('DISTINCT "users"."id" AS "User:id"');
28
+ });
29
+
30
+ it('will throw an exception if no field is present', () => {
31
+ expect(() => (new DistinctLiteral()).toString(connection)).toThrow(new TypeError('DistinctLiteral::fullyQualifiedNameToDefinition: Unable to find field for fully qualified name "undefined".'));
32
+ });
33
+
34
+ it('can turn a raw field into a projection field', () => {
35
+ expect((new DistinctLiteral(User.fields.firstName)).toString(connection)).toEqual('DISTINCT "users"."firstName" AS "User:firstName"');
36
+ });
37
+
38
+ it('can provide a SQL literal', () => {
39
+ expect((new DistinctLiteral(new Literal('test'))).toString(connection)).toEqual('DISTINCT test');
40
+ });
41
+ });
42
+ });
@@ -0,0 +1,26 @@
1
+ /* eslint-disable no-magic-numbers */
2
+
3
+ 'use strict';
4
+
5
+ /* global describe, it, expect, beforeAll */
6
+
7
+ const ConnectionBase = require('../../../lib/connection/connection-base');
8
+ const { Literal } = require('../../../lib/connection/literals');
9
+
10
+ describe('Literal', () => {
11
+ let connection;
12
+
13
+ beforeAll(async () => {
14
+ connection = new ConnectionBase({
15
+ bindModels: false,
16
+ models: require('../../support/models'),
17
+ });
18
+ });
19
+
20
+ describe('toString', () => {
21
+ it('can return anything as a literal', () => {
22
+ expect((new Literal('test')).toString(connection)).toEqual('test');
23
+ expect((new Literal('DERP(stuff)')).toString(connection)).toEqual('DERP(stuff)');
24
+ });
25
+ });
26
+ });
@@ -0,0 +1,42 @@
1
+ /* eslint-disable no-magic-numbers */
2
+
3
+ 'use strict';
4
+
5
+ /* global describe, it, expect, beforeAll */
6
+
7
+ const ConnectionBase = require('../../../lib/connection/connection-base');
8
+ const { MaxLiteral, Literal } = require('../../../lib/connection/literals');
9
+
10
+ describe('MaxLiteral', () => {
11
+ let connection;
12
+ let User;
13
+
14
+ beforeAll(async () => {
15
+ connection = new ConnectionBase({
16
+ bindModels: false,
17
+ models: require('../../support/models'),
18
+ });
19
+
20
+ let models = connection.getModels();
21
+
22
+ User = models.User;
23
+ });
24
+
25
+ describe('toString', () => {
26
+ it('can turn a fully qualified name into a min projection', () => {
27
+ expect((new MaxLiteral('User:id')).toString(connection)).toEqual('MAX("users"."id")');
28
+ });
29
+
30
+ it('will throw an exception if no field is present', () => {
31
+ expect(() => (new MaxLiteral()).toString(connection)).toThrow(new TypeError('MaxLiteral::fullyQualifiedNameToDefinition: Unable to find field for fully qualified name "undefined".'));
32
+ });
33
+
34
+ it('can turn a raw field into a projection field', () => {
35
+ expect((new MaxLiteral(User.fields.firstName)).toString(connection)).toEqual('MAX("users"."firstName")');
36
+ });
37
+
38
+ it('can provide a SQL literal', () => {
39
+ expect((new MaxLiteral(new Literal('test'))).toString(connection)).toEqual('MAX(test)');
40
+ });
41
+ });
42
+ });
@@ -0,0 +1,42 @@
1
+ /* eslint-disable no-magic-numbers */
2
+
3
+ 'use strict';
4
+
5
+ /* global describe, it, expect, beforeAll */
6
+
7
+ const ConnectionBase = require('../../../lib/connection/connection-base');
8
+ const { MinLiteral, Literal } = require('../../../lib/connection/literals');
9
+
10
+ describe('MinLiteral', () => {
11
+ let connection;
12
+ let User;
13
+
14
+ beforeAll(async () => {
15
+ connection = new ConnectionBase({
16
+ bindModels: false,
17
+ models: require('../../support/models'),
18
+ });
19
+
20
+ let models = connection.getModels();
21
+
22
+ User = models.User;
23
+ });
24
+
25
+ describe('toString', () => {
26
+ it('can turn a fully qualified name into a min projection', () => {
27
+ expect((new MinLiteral('User:id')).toString(connection)).toEqual('MIN("users"."id")');
28
+ });
29
+
30
+ it('will throw an exception if no field is present', () => {
31
+ expect(() => (new MinLiteral()).toString(connection)).toThrow(new TypeError('MinLiteral::fullyQualifiedNameToDefinition: Unable to find field for fully qualified name "undefined".'));
32
+ });
33
+
34
+ it('can turn a raw field into a projection field', () => {
35
+ expect((new MinLiteral(User.fields.firstName)).toString(connection)).toEqual('MIN("users"."firstName")');
36
+ });
37
+
38
+ it('can provide a SQL literal', () => {
39
+ expect((new MinLiteral(new Literal('test'))).toString(connection)).toEqual('MIN(test)');
40
+ });
41
+ });
42
+ });
@@ -0,0 +1,42 @@
1
+ /* eslint-disable no-magic-numbers */
2
+
3
+ 'use strict';
4
+
5
+ /* global describe, it, expect, beforeAll */
6
+
7
+ const ConnectionBase = require('../../../lib/connection/connection-base');
8
+ const { SumLiteral, Literal } = require('../../../lib/connection/literals');
9
+
10
+ describe('SumLiteral', () => {
11
+ let connection;
12
+ let User;
13
+
14
+ beforeAll(async () => {
15
+ connection = new ConnectionBase({
16
+ bindModels: false,
17
+ models: require('../../support/models'),
18
+ });
19
+
20
+ let models = connection.getModels();
21
+
22
+ User = models.User;
23
+ });
24
+
25
+ describe('toString', () => {
26
+ it('can turn a fully qualified name into a min projection', () => {
27
+ expect((new SumLiteral('User:id')).toString(connection)).toEqual('SUM("users"."id")');
28
+ });
29
+
30
+ it('will throw an exception if no field is present', () => {
31
+ expect(() => (new SumLiteral()).toString(connection)).toThrow(new TypeError('SumLiteral::fullyQualifiedNameToDefinition: Unable to find field for fully qualified name "undefined".'));
32
+ });
33
+
34
+ it('can turn a raw field into a projection field', () => {
35
+ expect((new SumLiteral(User.fields.firstName)).toString(connection)).toEqual('SUM("users"."firstName")');
36
+ });
37
+
38
+ it('can provide a SQL literal', () => {
39
+ expect((new SumLiteral(new Literal('test'))).toString(connection)).toEqual('SUM(test)');
40
+ });
41
+ });
42
+ });