foxhound 2.0.26 → 2.0.28
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.
- package/README.md +27 -25
- package/docs/README.md +28 -28
- package/docs/_brand.json +18 -0
- package/docs/_cover.md +2 -2
- package/docs/_topbar.md +1 -1
- package/docs/_version.json +7 -0
- package/docs/api/README.md +14 -14
- package/docs/api/behaviorFlags.md +1 -1
- package/docs/api/buildQuery.md +2 -2
- package/docs/api/clone.md +1 -1
- package/docs/api/setScope.md +1 -1
- package/docs/architecture.md +4 -4
- package/docs/dialects/README.md +9 -9
- package/docs/dialects/postgresql.md +1 -1
- package/docs/dialects/sqlite.md +5 -5
- package/docs/index.html +6 -7
- package/docs/joins.md +3 -3
- package/docs/query/README.md +4 -4
- package/docs/query/create.md +4 -4
- package/docs/query/update.md +10 -10
- package/docs/query-overrides.md +1 -1
- package/docs/quickstart.md +6 -6
- package/docs/retold-catalog.json +245 -161
- package/docs/retold-keyword-index.json +11916 -6383
- package/docs/schema.md +33 -33
- package/docs/sorting.md +4 -4
- package/package.json +7 -6
- package/source/Foxhound-Dialects.js +1 -0
- package/source/dialects/MicrosoftSQL/FoxHound-Dialect-MSSQL.js +120 -0
- package/source/dialects/Oracle/FoxHound-Dialect-Oracle.js +1218 -0
- package/test/Foxhound-Dialect-MSSQL_tests.js +59 -4
- package/test/Foxhound-Dialect-Oracle_tests.js +463 -0
- package/docs/css/docuserve.css +0 -73
|
@@ -618,7 +618,7 @@ suite
|
|
|
618
618
|
// This is the query generated by the MSSQL dialect
|
|
619
619
|
_Fable.log.trace('Select Query', tmpQuery.query);
|
|
620
620
|
Expect(tmpQuery.query.body)
|
|
621
|
-
.to.equal('SELECT [Name], [Age], [Cost] FROM [Animal] WHERE [Deleted] = @Deleted_w0 OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;');
|
|
621
|
+
.to.equal('SELECT [Name], [Age], [Cost] FROM [Animal] WHERE [Deleted] = @Deleted_w0 ORDER BY [IDAnimal] OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;');
|
|
622
622
|
}
|
|
623
623
|
);
|
|
624
624
|
test
|
|
@@ -640,7 +640,7 @@ suite
|
|
|
640
640
|
// This is the query generated by the MSSQL dialect
|
|
641
641
|
_Fable.log.trace('Select Query', tmpQuery.query);
|
|
642
642
|
Expect(tmpQuery.query.body)
|
|
643
|
-
.to.equal('SELECT DISTINCT [Name], [Age], [Cost] FROM [Animal] WHERE [Deleted] = @Deleted_w0 OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;');
|
|
643
|
+
.to.equal('SELECT DISTINCT [Name], [Age], [Cost] FROM [Animal] WHERE [Deleted] = @Deleted_w0 ORDER BY [IDAnimal] OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;');
|
|
644
644
|
}
|
|
645
645
|
);
|
|
646
646
|
test
|
|
@@ -669,7 +669,7 @@ suite
|
|
|
669
669
|
// This is the query generated by the MSSQL dialect
|
|
670
670
|
_Fable.log.trace('Select Query', tmpQuery.query);
|
|
671
671
|
Expect(tmpQuery.query.body)
|
|
672
|
-
.to.equal('SELECT [Name], [Age], [Cost] FROM [Animal] WHERE [Age] = @Age_w0 AND ( [Color] = @Color_w2 OR [Color] = @Color_w3 ) AND [Description] IS NOT NULL AND [IDOffice] IN ( @IDOffice_w6 ) AND [Deleted] = @Deleted_w7 OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;');
|
|
672
|
+
.to.equal('SELECT [Name], [Age], [Cost] FROM [Animal] WHERE [Age] = @Age_w0 AND ( [Color] = @Color_w2 OR [Color] = @Color_w3 ) AND [Description] IS NOT NULL AND [IDOffice] IN ( @IDOffice_w6 ) AND [Deleted] = @Deleted_w7 ORDER BY [IDAnimal] OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;');
|
|
673
673
|
}
|
|
674
674
|
);
|
|
675
675
|
test
|
|
@@ -691,7 +691,62 @@ suite
|
|
|
691
691
|
// This is the query generated by the MSSQL dialect
|
|
692
692
|
_Fable.log.trace('Select Query', tmpQuery.query);
|
|
693
693
|
Expect(tmpQuery.query.body)
|
|
694
|
-
.to.equal('SELECT [Name], [Age], [Cost] FROM [Animal] OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;');
|
|
694
|
+
.to.equal('SELECT [Name], [Age], [Cost] FROM [Animal] ORDER BY [IDAnimal] OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;');
|
|
695
|
+
}
|
|
696
|
+
);
|
|
697
|
+
test
|
|
698
|
+
(
|
|
699
|
+
'Read Query with legacyPagination uses ROW_NUMBER() wrapper (caller sort)',
|
|
700
|
+
function()
|
|
701
|
+
{
|
|
702
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
703
|
+
.setDialect('MSSQL')
|
|
704
|
+
.setScope('Animal')
|
|
705
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
706
|
+
.setCap(10)
|
|
707
|
+
.setBegin(20)
|
|
708
|
+
.setSort([{Column:'Age',Direction:'Ascending'}]);
|
|
709
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
710
|
+
tmpQuery.parameters.legacyPagination = true;
|
|
711
|
+
tmpQuery.buildReadQuery();
|
|
712
|
+
_Fable.log.trace('Legacy Select Query', tmpQuery.query);
|
|
713
|
+
Expect(tmpQuery.query.body)
|
|
714
|
+
.to.equal('SELECT [Name], [Age], [Cost] FROM (SELECT [Name], [Age], [Cost], ROW_NUMBER() OVER (ORDER BY [Age]) AS [_RowNum] FROM [Animal] WHERE [Deleted] = @Deleted_w0) AS [_Paged] WHERE [_RowNum] > 20 AND [_RowNum] <= 30;');
|
|
715
|
+
}
|
|
716
|
+
);
|
|
717
|
+
test
|
|
718
|
+
(
|
|
719
|
+
'Read Query with legacyPagination injects PK ORDER BY when caller omits sort',
|
|
720
|
+
function()
|
|
721
|
+
{
|
|
722
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
723
|
+
.setDialect('MSSQL')
|
|
724
|
+
.setScope('Animal')
|
|
725
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
726
|
+
.setCap(10);
|
|
727
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
728
|
+
tmpQuery.parameters.legacyPagination = true;
|
|
729
|
+
tmpQuery.buildReadQuery();
|
|
730
|
+
_Fable.log.trace('Legacy Select Query', tmpQuery.query);
|
|
731
|
+
Expect(tmpQuery.query.body)
|
|
732
|
+
.to.equal('SELECT [Name], [Age], [Cost] FROM (SELECT [Name], [Age], [Cost], ROW_NUMBER() OVER (ORDER BY [IDAnimal]) AS [_RowNum] FROM [Animal] WHERE [Deleted] = @Deleted_w0) AS [_Paged] WHERE [_RowNum] > 0 AND [_RowNum] <= 10;');
|
|
733
|
+
}
|
|
734
|
+
);
|
|
735
|
+
test
|
|
736
|
+
(
|
|
737
|
+
'Read Query with legacyPagination is inert without cap',
|
|
738
|
+
function()
|
|
739
|
+
{
|
|
740
|
+
// No cap → no pagination at all (legacy or otherwise);
|
|
741
|
+
// the flag should be a no-op and not produce a ROW_NUMBER wrapper.
|
|
742
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
743
|
+
.setDialect('MSSQL')
|
|
744
|
+
.setScope('Animal');
|
|
745
|
+
tmpQuery.addSort({Column:'Cost',Direction:'Descending'});
|
|
746
|
+
tmpQuery.parameters.legacyPagination = true;
|
|
747
|
+
tmpQuery.buildReadQuery();
|
|
748
|
+
Expect(tmpQuery.query.body)
|
|
749
|
+
.to.equal('SELECT [Animal].* FROM [Animal] ORDER BY [Cost] DESC;');
|
|
695
750
|
}
|
|
696
751
|
);
|
|
697
752
|
test
|
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for FoxHound Oracle Dialect
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
var Chai = require('chai');
|
|
10
|
+
var Expect = Chai.expect;
|
|
11
|
+
|
|
12
|
+
var libFable = require('fable');
|
|
13
|
+
const _Fable = new libFable({Product:'FoxhoundTestsOracle'});
|
|
14
|
+
var libFoxHound = require('../source/FoxHound.js');
|
|
15
|
+
|
|
16
|
+
var _AnimalSchema = (
|
|
17
|
+
[
|
|
18
|
+
{ Column: "IDAnimal", Type:"AutoIdentity" },
|
|
19
|
+
{ Column: "GUIDAnimal", Type:"AutoGUID" },
|
|
20
|
+
{ Column: "CreateDate", Type:"CreateDate" },
|
|
21
|
+
{ Column: "CreatingIDUser", Type:"CreateIDUser" },
|
|
22
|
+
{ Column: "UpdateDate", Type:"UpdateDate" },
|
|
23
|
+
{ Column: "UpdatingIDUser", Type:"UpdateIDUser" },
|
|
24
|
+
{ Column: "Deleted", Type:"Deleted" },
|
|
25
|
+
{ Column: "DeletingIDUser", Type:"DeleteIDUser" },
|
|
26
|
+
{ Column: "DeleteDate", Type:"DeleteDate" },
|
|
27
|
+
{ Column: "Name", Type:"String" },
|
|
28
|
+
{ Column: "Age", Type:"Integer" }
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
var _AnimalSchemaWithoutDeleted = (
|
|
32
|
+
[
|
|
33
|
+
{ Column: "IDAnimal", Type:"AutoIdentity" },
|
|
34
|
+
{ Column: "GUIDAnimal", Type:"AutoGUID" },
|
|
35
|
+
{ Column: "CreateDate", Type:"CreateDate" },
|
|
36
|
+
{ Column: "CreatingIDUser", Type:"CreateIDUser" },
|
|
37
|
+
{ Column: "UpdateDate", Type:"UpdateDate" },
|
|
38
|
+
{ Column: "UpdatingIDUser", Type:"UpdateIDUser" }
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
suite
|
|
42
|
+
(
|
|
43
|
+
'FoxHound-Dialect-Oracle',
|
|
44
|
+
function()
|
|
45
|
+
{
|
|
46
|
+
suite
|
|
47
|
+
(
|
|
48
|
+
'Object Sanity',
|
|
49
|
+
function()
|
|
50
|
+
{
|
|
51
|
+
test
|
|
52
|
+
(
|
|
53
|
+
'initialize should build a happy little object',
|
|
54
|
+
function()
|
|
55
|
+
{
|
|
56
|
+
var testFoxHound = libFoxHound.new(_Fable).setDialect('Oracle');
|
|
57
|
+
Expect(testFoxHound.dialect.name)
|
|
58
|
+
.to.equal('Oracle');
|
|
59
|
+
Expect(testFoxHound)
|
|
60
|
+
.to.be.an('object', 'FoxHound with Oracle should initialize as an object directly from the require statement.');
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
suite
|
|
67
|
+
(
|
|
68
|
+
'Create Query Generation',
|
|
69
|
+
function()
|
|
70
|
+
{
|
|
71
|
+
test
|
|
72
|
+
(
|
|
73
|
+
'Create Query without schema parameterizes every column and has no RETURNING',
|
|
74
|
+
function()
|
|
75
|
+
{
|
|
76
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
77
|
+
.setDialect('Oracle')
|
|
78
|
+
.setScope('Animal')
|
|
79
|
+
.addRecord({IDAnimal:null, Name:'Foo Foo', Age:15});
|
|
80
|
+
tmpQuery.buildCreateQuery();
|
|
81
|
+
Expect(tmpQuery.query.body)
|
|
82
|
+
.to.equal("INSERT INTO Animal ( IDAnimal, Name, Age) VALUES ( :IDAnimal_0, :Name_1, :Age_2)");
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
test
|
|
86
|
+
(
|
|
87
|
+
'Create Query with schema skips AutoIdentity and appends RETURNING INTO',
|
|
88
|
+
function()
|
|
89
|
+
{
|
|
90
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
91
|
+
.setDialect('Oracle')
|
|
92
|
+
.setScope('Animal')
|
|
93
|
+
.addRecord({IDAnimal:null, Name:'Foo Foo', Age:15});
|
|
94
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
95
|
+
tmpQuery.buildCreateQuery();
|
|
96
|
+
Expect(tmpQuery.query.body)
|
|
97
|
+
.to.equal("INSERT INTO Animal ( Name, Age) VALUES ( :Name_0, :Age_1) RETURNING IDAnimal INTO :RETURNING_ID");
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
test
|
|
101
|
+
(
|
|
102
|
+
'Bad Create Query returns false',
|
|
103
|
+
function()
|
|
104
|
+
{
|
|
105
|
+
var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle');
|
|
106
|
+
tmpQuery.buildCreateQuery();
|
|
107
|
+
tmpQuery.addRecord({});
|
|
108
|
+
tmpQuery.buildCreateQuery();
|
|
109
|
+
Expect(tmpQuery.query.body)
|
|
110
|
+
.to.equal(false);
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
suite
|
|
117
|
+
(
|
|
118
|
+
'Read Query Generation',
|
|
119
|
+
function()
|
|
120
|
+
{
|
|
121
|
+
test
|
|
122
|
+
(
|
|
123
|
+
'Simple Read Query (no quoting)',
|
|
124
|
+
function()
|
|
125
|
+
{
|
|
126
|
+
var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle').setScope('Animal');
|
|
127
|
+
tmpQuery.addSort({Column:'Cost',Direction:'Descending'});
|
|
128
|
+
tmpQuery.buildReadQuery();
|
|
129
|
+
Expect(tmpQuery.query.body)
|
|
130
|
+
.to.equal('SELECT Animal.* FROM Animal ORDER BY Cost DESC');
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
test
|
|
134
|
+
(
|
|
135
|
+
'Read Query with named binds and OFFSET/FETCH pagination',
|
|
136
|
+
function()
|
|
137
|
+
{
|
|
138
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
139
|
+
.setDialect('Oracle')
|
|
140
|
+
.setScope('Animal')
|
|
141
|
+
.setDataElements(['Name', 'Age'])
|
|
142
|
+
.setCap(10)
|
|
143
|
+
.setBegin(0)
|
|
144
|
+
.addFilter('Age', '15')
|
|
145
|
+
.addSort('Age');
|
|
146
|
+
tmpQuery.buildReadQuery();
|
|
147
|
+
Expect(tmpQuery.query.body)
|
|
148
|
+
.to.equal('SELECT Name, Age FROM Animal WHERE Age = :Age_w0 ORDER BY Age OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY');
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
test
|
|
152
|
+
(
|
|
153
|
+
'Complex Read Query expands IN lists into discrete binds',
|
|
154
|
+
function()
|
|
155
|
+
{
|
|
156
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
157
|
+
.setDialect('Oracle')
|
|
158
|
+
.setScope('Animal')
|
|
159
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
160
|
+
.setCap(100)
|
|
161
|
+
.addFilter('Age', '25')
|
|
162
|
+
.addFilter('', '', '(')
|
|
163
|
+
.addFilter('Color', 'Red')
|
|
164
|
+
.addFilter('Color', 'Green', '=', 'OR')
|
|
165
|
+
.addFilter('', '', ')')
|
|
166
|
+
.addFilter('Description', '', 'IS NOT NULL')
|
|
167
|
+
.addFilter('IDOffice', [10, 11, 15, 18, 22], 'IN');
|
|
168
|
+
tmpQuery.addSort('Age');
|
|
169
|
+
tmpQuery.buildReadQuery();
|
|
170
|
+
Expect(tmpQuery.query.body)
|
|
171
|
+
.to.equal('SELECT Name, Age, Cost FROM Animal WHERE Age = :Age_w0 AND ( Color = :Color_w2 OR Color = :Color_w3 ) AND Description IS NOT NULL AND IDOffice IN (:IDOffice_w6_0, :IDOffice_w6_1, :IDOffice_w6_2, :IDOffice_w6_3, :IDOffice_w6_4) ORDER BY Age OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY');
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
test
|
|
175
|
+
(
|
|
176
|
+
'Read Query with schema injects soft-delete filter and PK ORDER BY',
|
|
177
|
+
function()
|
|
178
|
+
{
|
|
179
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
180
|
+
.setDialect('Oracle')
|
|
181
|
+
.setScope('Animal')
|
|
182
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
183
|
+
.setCap(100);
|
|
184
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
185
|
+
tmpQuery.buildReadQuery();
|
|
186
|
+
Expect(tmpQuery.query.body)
|
|
187
|
+
.to.equal('SELECT Name, Age, Cost FROM Animal WHERE Deleted = :Deleted_w0 ORDER BY IDAnimal OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY');
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
test
|
|
191
|
+
(
|
|
192
|
+
'Read Query with quoteIdentifiers wraps identifiers in double quotes',
|
|
193
|
+
function()
|
|
194
|
+
{
|
|
195
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
196
|
+
.setDialect('Oracle')
|
|
197
|
+
.setScope('Animal')
|
|
198
|
+
.setDataElements(['Name', 'Age'])
|
|
199
|
+
.addSort('Age');
|
|
200
|
+
tmpQuery.parameters.quoteIdentifiers = true;
|
|
201
|
+
tmpQuery.buildReadQuery();
|
|
202
|
+
Expect(tmpQuery.query.body)
|
|
203
|
+
.to.equal('SELECT "Name", "Age" FROM "Animal" ORDER BY "Age"');
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
test
|
|
207
|
+
(
|
|
208
|
+
'Custom Read Query honors the query override template',
|
|
209
|
+
function()
|
|
210
|
+
{
|
|
211
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
212
|
+
.setDialect('Oracle')
|
|
213
|
+
.setScope('Animal')
|
|
214
|
+
.setCap(10)
|
|
215
|
+
.setBegin(0)
|
|
216
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
217
|
+
.setFilter({Column:'Age',Operator:'=',Value:'15',Connector:'AND',Parameter:'Age'});
|
|
218
|
+
tmpQuery.parameters.queryOverride = 'SELECT Name, Age * 5, Cost FROM <%= TableName %> <%= Where %> <%= Limit %>';
|
|
219
|
+
tmpQuery.buildReadQuery();
|
|
220
|
+
Expect(tmpQuery.query.body)
|
|
221
|
+
.to.equal('SELECT Name, Age * 5, Cost FROM Animal WHERE Age = :Age_w0 OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY');
|
|
222
|
+
}
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
suite
|
|
228
|
+
(
|
|
229
|
+
'Legacy Pagination (ROWNUM double-subquery)',
|
|
230
|
+
function()
|
|
231
|
+
{
|
|
232
|
+
test
|
|
233
|
+
(
|
|
234
|
+
'legacyPagination wraps the query in a ROWNUM subquery (caller sort)',
|
|
235
|
+
function()
|
|
236
|
+
{
|
|
237
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
238
|
+
.setDialect('Oracle')
|
|
239
|
+
.setScope('Animal')
|
|
240
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
241
|
+
.setCap(10)
|
|
242
|
+
.setBegin(20)
|
|
243
|
+
.addSort('Age');
|
|
244
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
245
|
+
tmpQuery.parameters.legacyPagination = true;
|
|
246
|
+
tmpQuery.buildReadQuery();
|
|
247
|
+
Expect(tmpQuery.query.body)
|
|
248
|
+
.to.equal('SELECT Name, Age, Cost FROM (SELECT meadow_inner.*, ROWNUM AS "_RowNum" FROM (SELECT Name, Age, Cost FROM Animal WHERE Deleted = :Deleted_w0 ORDER BY Age) meadow_inner WHERE ROWNUM <= 30) WHERE "_RowNum" > 20');
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
test
|
|
252
|
+
(
|
|
253
|
+
'legacyPagination injects PK ORDER BY when caller omits sort',
|
|
254
|
+
function()
|
|
255
|
+
{
|
|
256
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
257
|
+
.setDialect('Oracle')
|
|
258
|
+
.setScope('Animal')
|
|
259
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
260
|
+
.setCap(10);
|
|
261
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
262
|
+
tmpQuery.parameters.legacyPagination = true;
|
|
263
|
+
tmpQuery.buildReadQuery();
|
|
264
|
+
Expect(tmpQuery.query.body)
|
|
265
|
+
.to.equal('SELECT Name, Age, Cost FROM (SELECT meadow_inner.*, ROWNUM AS "_RowNum" FROM (SELECT Name, Age, Cost FROM Animal WHERE Deleted = :Deleted_w0 ORDER BY IDAnimal) meadow_inner WHERE ROWNUM <= 10) WHERE "_RowNum" > 0');
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
test
|
|
269
|
+
(
|
|
270
|
+
'legacyPagination is inert without a cap',
|
|
271
|
+
function()
|
|
272
|
+
{
|
|
273
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
274
|
+
.setDialect('Oracle')
|
|
275
|
+
.setScope('Animal')
|
|
276
|
+
.setDataElements(['Name']);
|
|
277
|
+
tmpQuery.parameters.legacyPagination = true;
|
|
278
|
+
tmpQuery.buildReadQuery();
|
|
279
|
+
Expect(tmpQuery.query.body)
|
|
280
|
+
.to.equal('SELECT Name FROM Animal');
|
|
281
|
+
}
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
suite
|
|
287
|
+
(
|
|
288
|
+
'Update / Delete / Undelete Query Generation',
|
|
289
|
+
function()
|
|
290
|
+
{
|
|
291
|
+
test
|
|
292
|
+
(
|
|
293
|
+
'Update Query auto-stamps UpdateDate with SYS_EXTRACT_UTC',
|
|
294
|
+
function()
|
|
295
|
+
{
|
|
296
|
+
var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle')
|
|
297
|
+
.setScope('Animal')
|
|
298
|
+
.addFilter('IDAnimal', 9)
|
|
299
|
+
.addRecord({
|
|
300
|
+
IDAnimal:82,
|
|
301
|
+
GUIDAnimal:'1111-2222-3333-4444-5555-6666-7777',
|
|
302
|
+
CreateDate:false,
|
|
303
|
+
CreatingIDUser:false,
|
|
304
|
+
UpdateDate:false,
|
|
305
|
+
UpdatingIDUser:false,
|
|
306
|
+
Name:'Froo Froo',
|
|
307
|
+
Age:18
|
|
308
|
+
});
|
|
309
|
+
tmpQuery.query.schema = _AnimalSchemaWithoutDeleted;
|
|
310
|
+
tmpQuery.buildUpdateQuery();
|
|
311
|
+
Expect(tmpQuery.query.body)
|
|
312
|
+
.to.equal('UPDATE Animal SET GUIDAnimal = :GUIDAnimal_0, UpdateDate = SYS_EXTRACT_UTC(SYSTIMESTAMP), UpdatingIDUser = :UpdatingIDUser_2, Name = :Name_3, Age = :Age_4 WHERE IDAnimal = :IDAnimal_w0');
|
|
313
|
+
}
|
|
314
|
+
);
|
|
315
|
+
test
|
|
316
|
+
(
|
|
317
|
+
'Update Query with disabled stamps parameterizes UpdateDate',
|
|
318
|
+
function()
|
|
319
|
+
{
|
|
320
|
+
var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle')
|
|
321
|
+
.setScope('Animal')
|
|
322
|
+
.addFilter('IDAnimal', 9)
|
|
323
|
+
.setDisableAutoUserStamp(true)
|
|
324
|
+
.setDisableAutoDateStamp(true)
|
|
325
|
+
.addRecord({
|
|
326
|
+
IDAnimal:82,
|
|
327
|
+
GUIDAnimal:'1111-2222-3333-4444-5555-6666-7777',
|
|
328
|
+
CreateDate:false,
|
|
329
|
+
CreatingIDUser:false,
|
|
330
|
+
UpdateDate:false,
|
|
331
|
+
UpdatingIDUser:false,
|
|
332
|
+
Name:'Froo Froo',
|
|
333
|
+
Age:18
|
|
334
|
+
});
|
|
335
|
+
tmpQuery.query.schema = _AnimalSchemaWithoutDeleted;
|
|
336
|
+
tmpQuery.buildUpdateQuery();
|
|
337
|
+
Expect(tmpQuery.query.body)
|
|
338
|
+
.to.equal('UPDATE Animal SET GUIDAnimal = :GUIDAnimal_0, UpdateDate = :MANUAL_UpdateDate, Name = :Name_2, Age = :Age_3 WHERE IDAnimal = :IDAnimal_w0');
|
|
339
|
+
}
|
|
340
|
+
);
|
|
341
|
+
test
|
|
342
|
+
(
|
|
343
|
+
'Delete Query converts to soft-delete UPDATE when a Deleted column exists',
|
|
344
|
+
function()
|
|
345
|
+
{
|
|
346
|
+
var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle')
|
|
347
|
+
.setScope('Animal')
|
|
348
|
+
.addFilter('IDAnimal', 10);
|
|
349
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
350
|
+
tmpQuery.buildDeleteQuery();
|
|
351
|
+
Expect(tmpQuery.query.body)
|
|
352
|
+
.to.equal('UPDATE Animal SET UpdateDate = SYS_EXTRACT_UTC(SYSTIMESTAMP), Deleted = 1, DeletingIDUser = :DeletingIDUser_2, DeleteDate = SYS_EXTRACT_UTC(SYSTIMESTAMP) WHERE IDAnimal = :IDAnimal_w0 AND Deleted = :Deleted_w1');
|
|
353
|
+
}
|
|
354
|
+
);
|
|
355
|
+
test
|
|
356
|
+
(
|
|
357
|
+
'Delete Query is a hard DELETE when there is no Deleted column',
|
|
358
|
+
function()
|
|
359
|
+
{
|
|
360
|
+
var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle')
|
|
361
|
+
.setScope('Animal')
|
|
362
|
+
.addFilter('IDAnimal', 9)
|
|
363
|
+
.addRecord({IDAnimal:82, GUIDAnimal:'1111-2222'});
|
|
364
|
+
tmpQuery.query.schema = _AnimalSchemaWithoutDeleted;
|
|
365
|
+
tmpQuery.buildDeleteQuery();
|
|
366
|
+
Expect(tmpQuery.query.body)
|
|
367
|
+
.to.equal('DELETE FROM Animal WHERE IDAnimal = :IDAnimal_w0');
|
|
368
|
+
}
|
|
369
|
+
);
|
|
370
|
+
test
|
|
371
|
+
(
|
|
372
|
+
'Delete Query is a hard DELETE when delete tracking is disabled',
|
|
373
|
+
function()
|
|
374
|
+
{
|
|
375
|
+
var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle')
|
|
376
|
+
.setScope('Animal')
|
|
377
|
+
.setDisableDeleteTracking(true)
|
|
378
|
+
.addFilter('IDAnimal', 9)
|
|
379
|
+
.addRecord({IDAnimal:82, GUIDAnimal:'1111-2222'});
|
|
380
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
381
|
+
tmpQuery.buildDeleteQuery();
|
|
382
|
+
Expect(tmpQuery.query.body)
|
|
383
|
+
.to.equal('DELETE FROM Animal WHERE IDAnimal = :IDAnimal_w0');
|
|
384
|
+
}
|
|
385
|
+
);
|
|
386
|
+
test
|
|
387
|
+
(
|
|
388
|
+
'Undelete Query clears the Deleted bit',
|
|
389
|
+
function()
|
|
390
|
+
{
|
|
391
|
+
var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle')
|
|
392
|
+
.setScope('Animal')
|
|
393
|
+
.addFilter('IDAnimal', 10);
|
|
394
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
395
|
+
tmpQuery.buildUndeleteQuery();
|
|
396
|
+
Expect(tmpQuery.query.body)
|
|
397
|
+
.to.equal('UPDATE Animal SET UpdateDate = SYS_EXTRACT_UTC(SYSTIMESTAMP), UpdatingIDUser = :UpdatingIDUser_1, Deleted = 0 WHERE IDAnimal = :IDAnimal_w0');
|
|
398
|
+
}
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
suite
|
|
404
|
+
(
|
|
405
|
+
'Count Query Generation',
|
|
406
|
+
function()
|
|
407
|
+
{
|
|
408
|
+
test
|
|
409
|
+
(
|
|
410
|
+
'Count Query injects soft-delete filter and aliases as RowCount',
|
|
411
|
+
function()
|
|
412
|
+
{
|
|
413
|
+
var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle').setScope('Animal');
|
|
414
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
415
|
+
tmpQuery.buildCountQuery();
|
|
416
|
+
Expect(tmpQuery.query.body)
|
|
417
|
+
.to.equal('SELECT COUNT(*) AS RowCount FROM Animal WHERE Deleted = :Deleted_w0');
|
|
418
|
+
}
|
|
419
|
+
);
|
|
420
|
+
test
|
|
421
|
+
(
|
|
422
|
+
'Count Query honors DISTINCT on the selected field',
|
|
423
|
+
function()
|
|
424
|
+
{
|
|
425
|
+
var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle')
|
|
426
|
+
.setScope('Animal')
|
|
427
|
+
.setDataElements(['Name'])
|
|
428
|
+
.setDistinct(true);
|
|
429
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
430
|
+
tmpQuery.buildCountQuery();
|
|
431
|
+
Expect(tmpQuery.query.body)
|
|
432
|
+
.to.equal('SELECT COUNT(DISTINCT Name) AS RowCount FROM Animal WHERE Deleted = :Deleted_w0');
|
|
433
|
+
}
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
suite
|
|
439
|
+
(
|
|
440
|
+
'Oracle Bind Type Tracking',
|
|
441
|
+
function()
|
|
442
|
+
{
|
|
443
|
+
test
|
|
444
|
+
(
|
|
445
|
+
'parameterTypes maps schema types to oracledb type strings',
|
|
446
|
+
function()
|
|
447
|
+
{
|
|
448
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
449
|
+
.setDialect('Oracle')
|
|
450
|
+
.setScope('Animal')
|
|
451
|
+
.addFilter('Age', 15)
|
|
452
|
+
.addFilter('Name', 'Foo');
|
|
453
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
454
|
+
tmpQuery.buildReadQuery();
|
|
455
|
+
Expect(tmpQuery.query.parameterTypes.Age_w0).to.equal('NUMBER');
|
|
456
|
+
Expect(tmpQuery.query.parameterTypes.Name_w1).to.equal('STRING');
|
|
457
|
+
Expect(tmpQuery.query.parameterTypes.Deleted_w2).to.equal('NUMBER');
|
|
458
|
+
}
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
);
|
package/docs/css/docuserve.css
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/* ============================================================================
|
|
2
|
-
Pict Docuserve - Base Styles
|
|
3
|
-
============================================================================ */
|
|
4
|
-
|
|
5
|
-
/* Reset and base */
|
|
6
|
-
*, *::before, *::after {
|
|
7
|
-
box-sizing: border-box;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
html, body {
|
|
11
|
-
margin: 0;
|
|
12
|
-
padding: 0;
|
|
13
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
14
|
-
font-size: 16px;
|
|
15
|
-
line-height: 1.5;
|
|
16
|
-
color: #423D37;
|
|
17
|
-
background-color: #fff;
|
|
18
|
-
-webkit-font-smoothing: antialiased;
|
|
19
|
-
-moz-osx-font-smoothing: grayscale;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/* Typography */
|
|
23
|
-
h1, h2, h3, h4, h5, h6 {
|
|
24
|
-
margin-top: 0;
|
|
25
|
-
line-height: 1.3;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
a {
|
|
29
|
-
color: #2E7D74;
|
|
30
|
-
text-decoration: none;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
a:hover {
|
|
34
|
-
color: #256861;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/* Application container */
|
|
38
|
-
#Docuserve-Application-Container {
|
|
39
|
-
min-height: 100vh;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/* Utility: scrollbar styling */
|
|
43
|
-
::-webkit-scrollbar {
|
|
44
|
-
width: 8px;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
::-webkit-scrollbar-track {
|
|
48
|
-
background: #F5F0E8;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
::-webkit-scrollbar-thumb {
|
|
52
|
-
background: #D4CCBE;
|
|
53
|
-
border-radius: 4px;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
::-webkit-scrollbar-thumb:hover {
|
|
57
|
-
background: #B5AA9A;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/* Responsive adjustments */
|
|
61
|
-
@media (max-width: 768px) {
|
|
62
|
-
html {
|
|
63
|
-
font-size: 14px;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
#Docuserve-Sidebar-Container {
|
|
67
|
-
display: none;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.docuserve-body {
|
|
71
|
-
flex-direction: column;
|
|
72
|
-
}
|
|
73
|
-
}
|