@objectql/server 3.0.1 → 4.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.
- package/CHANGELOG.md +18 -3
- package/dist/adapters/graphql.d.ts +7 -0
- package/dist/adapters/graphql.js +8 -1
- package/dist/adapters/graphql.js.map +1 -1
- package/dist/adapters/node.d.ts +7 -0
- package/dist/adapters/node.js +7 -0
- package/dist/adapters/node.js.map +1 -1
- package/dist/adapters/rest.d.ts +7 -0
- package/dist/adapters/rest.js +7 -0
- package/dist/adapters/rest.js.map +1 -1
- package/dist/file-handler.d.ts +7 -0
- package/dist/file-handler.js +7 -0
- package/dist/file-handler.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/metadata.d.ts +7 -0
- package/dist/metadata.js +10 -3
- package/dist/metadata.js.map +1 -1
- package/dist/openapi.d.ts +7 -0
- package/dist/openapi.js +7 -0
- package/dist/openapi.js.map +1 -1
- package/dist/server.d.ts +7 -0
- package/dist/server.js +14 -2
- package/dist/server.js.map +1 -1
- package/dist/storage.d.ts +7 -0
- package/dist/storage.js +7 -0
- package/dist/storage.js.map +1 -1
- package/dist/templates.d.ts +7 -0
- package/dist/templates.js +7 -0
- package/dist/templates.js.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.js +8 -1
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +7 -0
- package/dist/utils.js +8 -1
- package/dist/utils.js.map +1 -1
- package/jest.config.js +8 -0
- package/package.json +3 -3
- package/src/adapters/graphql.ts +9 -1
- package/src/adapters/node.ts +8 -0
- package/src/adapters/rest.ts +8 -0
- package/src/file-handler.ts +8 -0
- package/src/index.ts +8 -0
- package/src/metadata.ts +11 -3
- package/src/openapi.ts +8 -0
- package/src/server.ts +13 -2
- package/src/storage.ts +8 -0
- package/src/templates.ts +8 -0
- package/src/types.ts +8 -0
- package/src/utils.ts +8 -0
- package/test/custom-routes.test.ts +8 -0
- package/test/file-upload-integration.example.ts +8 -0
- package/test/file-validation.test.ts +8 -0
- package/test/graphql.test.ts +10 -0
- package/test/integration-example.ts +8 -0
- package/test/metadata.test.ts +8 -0
- package/test/node.test.ts +10 -0
- package/test/openapi.test.ts +8 -0
- package/test/rest-advanced.test.ts +19 -4
- package/test/rest.test.ts +96 -20
- package/test/storage.test.ts +8 -0
- package/tsconfig.tsbuildinfo +1 -1
package/test/rest.test.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectQL
|
|
3
|
+
* Copyright (c) 2026-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
import request from 'supertest';
|
|
2
10
|
import { createServer } from 'http';
|
|
3
11
|
import { ObjectQL } from '@objectql/core';
|
|
@@ -19,37 +27,97 @@ class MockDriver implements Driver {
|
|
|
19
27
|
async find(objectName: string, query: any) {
|
|
20
28
|
let items = this.data[objectName] || [];
|
|
21
29
|
|
|
22
|
-
// Apply filters if provided
|
|
30
|
+
// Apply filters if provided (supports FilterNode array format)
|
|
23
31
|
if (query && query.filters) {
|
|
24
|
-
|
|
25
|
-
if (typeof filters === 'object') {
|
|
26
|
-
const filterKeys = Object.keys(filters);
|
|
27
|
-
if (filterKeys.length > 0) {
|
|
28
|
-
items = items.filter(item => {
|
|
29
|
-
for (const [key, value] of Object.entries(filters)) {
|
|
30
|
-
if (item[key] !== value) {
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return true;
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
}
|
|
32
|
+
items = items.filter(item => this.matchesFilter(item, query.filters));
|
|
38
33
|
}
|
|
39
34
|
|
|
40
|
-
// Apply skip and limit if provided
|
|
35
|
+
// Apply skip and top/limit if provided (QueryAST uses 'top' for limit)
|
|
41
36
|
if (query) {
|
|
42
37
|
if (query.skip) {
|
|
43
38
|
items = items.slice(query.skip);
|
|
44
39
|
}
|
|
45
|
-
if (query.limit) {
|
|
46
|
-
items = items.slice(0, query.limit);
|
|
40
|
+
if (query.top || query.limit) {
|
|
41
|
+
items = items.slice(0, query.top || query.limit);
|
|
47
42
|
}
|
|
48
43
|
}
|
|
49
44
|
|
|
50
45
|
return items;
|
|
51
46
|
}
|
|
52
|
-
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Matches an item against a filter condition
|
|
50
|
+
* @param item - The data item to test
|
|
51
|
+
* @param filter - The filter in FilterNode array format or simple object format
|
|
52
|
+
* FilterNode format: [field, op, value] for single condition
|
|
53
|
+
* Complex filters: [[field, op, value], 'and', [field2, op2, value2]]
|
|
54
|
+
* Simple format: { field: value }
|
|
55
|
+
* @returns true if item matches the filter
|
|
56
|
+
*/
|
|
57
|
+
private matchesFilter(item: any, filter: any): boolean {
|
|
58
|
+
if (!filter) return true;
|
|
59
|
+
|
|
60
|
+
// Handle FilterNode array format: [[field, op, value]] or [[field, op, value], 'and', [field2, op2, value2]]
|
|
61
|
+
if (Array.isArray(filter)) {
|
|
62
|
+
// Single condition: [field, op, value]
|
|
63
|
+
if (filter.length === 3 && typeof filter[0] === 'string') {
|
|
64
|
+
const [field, op, value] = filter;
|
|
65
|
+
return this.evaluateCondition(item, field, op, value);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Multiple conditions with logical operators
|
|
69
|
+
let result = true;
|
|
70
|
+
let currentOp = 'and';
|
|
71
|
+
for (let i = 0; i < filter.length; i++) {
|
|
72
|
+
const element = filter[i];
|
|
73
|
+
if (typeof element === 'string') {
|
|
74
|
+
currentOp = element; // 'and' or 'or'
|
|
75
|
+
} else if (Array.isArray(element)) {
|
|
76
|
+
const conditionResult = this.matchesFilter(item, element);
|
|
77
|
+
if (currentOp === 'and') {
|
|
78
|
+
result = result && conditionResult;
|
|
79
|
+
} else if (currentOp === 'or') {
|
|
80
|
+
result = result || conditionResult;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Handle simple object format: { field: value }
|
|
88
|
+
if (typeof filter === 'object') {
|
|
89
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
90
|
+
if (item[key] !== value) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Evaluates a single filter condition
|
|
102
|
+
* @param item - The data item to test
|
|
103
|
+
* @param field - The field name
|
|
104
|
+
* @param op - The operator: '=', '!=', '>', '>=', '<', '<=', 'in'
|
|
105
|
+
* @param value - The value to compare against
|
|
106
|
+
* @returns true if the condition is satisfied
|
|
107
|
+
*/
|
|
108
|
+
private evaluateCondition(item: any, field: string, op: string, value: any): boolean {
|
|
109
|
+
const fieldValue = item[field];
|
|
110
|
+
switch (op) {
|
|
111
|
+
case '=': return fieldValue === value;
|
|
112
|
+
case '!=': return fieldValue !== value;
|
|
113
|
+
case '>': return fieldValue > value;
|
|
114
|
+
case '>=': return fieldValue >= value;
|
|
115
|
+
case '<': return fieldValue < value;
|
|
116
|
+
case '<=': return fieldValue <= value;
|
|
117
|
+
case 'in': return Array.isArray(value) ? value.includes(fieldValue) : false;
|
|
118
|
+
default: return true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
53
121
|
async findOne(objectName: string, id: string | number, query?: any, options?: any) {
|
|
54
122
|
const items = this.data[objectName] || [];
|
|
55
123
|
if (id !== undefined && id !== null) {
|
|
@@ -89,7 +157,13 @@ class MockDriver implements Driver {
|
|
|
89
157
|
}
|
|
90
158
|
|
|
91
159
|
async count(objectName: string, query: any) {
|
|
92
|
-
|
|
160
|
+
// Count should apply filters but not skip/limit
|
|
161
|
+
const countQuery = { ...query };
|
|
162
|
+
delete countQuery.skip;
|
|
163
|
+
delete countQuery.top;
|
|
164
|
+
delete countQuery.limit;
|
|
165
|
+
const items = await this.find(objectName, countQuery);
|
|
166
|
+
return items.length;
|
|
93
167
|
}
|
|
94
168
|
|
|
95
169
|
async createMany(objectName: string, data: any[]) {
|
|
@@ -193,6 +267,8 @@ describe('REST API Adapter', () => {
|
|
|
193
267
|
}
|
|
194
268
|
}
|
|
195
269
|
});
|
|
270
|
+
|
|
271
|
+
await app.init();
|
|
196
272
|
|
|
197
273
|
// Create handler and server once for all tests
|
|
198
274
|
handler = createRESTHandler(app);
|
package/test/storage.test.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectQL
|
|
3
|
+
* Copyright (c) 2026-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
import { MemoryFileStorage } from '../src/storage';
|
|
2
10
|
import { AttachmentData } from '../src/types';
|
|
3
11
|
|