@objectql/server 1.8.2 → 1.8.4
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 +21 -0
- package/dist/adapters/node.d.ts +3 -1
- package/dist/adapters/node.js +35 -29
- package/dist/adapters/node.js.map +1 -1
- package/dist/adapters/rest.d.ts +20 -10
- package/dist/adapters/rest.js +39 -31
- package/dist/adapters/rest.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/metadata.d.ts +12 -2
- package/dist/metadata.js +31 -23
- package/dist/metadata.js.map +1 -1
- package/dist/openapi.d.ts +2 -2
- package/dist/openapi.js +6 -4
- package/dist/openapi.js.map +1 -1
- package/dist/utils.d.ts +15 -0
- package/dist/utils.js +24 -0
- package/dist/utils.js.map +1 -0
- package/package.json +3 -3
- package/src/adapters/node.ts +26 -19
- package/src/adapters/rest.ts +34 -19
- package/src/index.ts +1 -1
- package/src/metadata.ts +29 -14
- package/src/openapi.ts +6 -5
- package/src/utils.ts +21 -0
- package/test/custom-routes.test.ts +297 -0
- package/test/metadata.test.ts +259 -0
- package/test/openapi.test.ts +354 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/studio.d.ts +0 -5
- package/dist/studio.js +0 -186
- package/dist/studio.js.map +0 -1
- package/src/studio.ts +0 -164
package/dist/metadata.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createMetadataHandler = createMetadataHandler;
|
|
4
|
-
const types_1 = require("
|
|
4
|
+
const types_1 = require("@objectql/types");
|
|
5
|
+
const types_2 = require("./types");
|
|
6
|
+
const utils_1 = require("./utils");
|
|
5
7
|
function readBody(req) {
|
|
6
8
|
return new Promise((resolve, reject) => {
|
|
7
9
|
let body = '';
|
|
@@ -22,8 +24,13 @@ function readBody(req) {
|
|
|
22
24
|
/**
|
|
23
25
|
* Creates a handler for metadata endpoints.
|
|
24
26
|
* These endpoints expose information about registered objects and other metadata.
|
|
27
|
+
*
|
|
28
|
+
* @param app - ObjectQL application instance
|
|
29
|
+
* @param options - Optional configuration including custom routes
|
|
25
30
|
*/
|
|
26
|
-
function createMetadataHandler(app) {
|
|
31
|
+
function createMetadataHandler(app, options) {
|
|
32
|
+
const routes = (0, types_1.resolveApiRoutes)(options === null || options === void 0 ? void 0 : options.routes);
|
|
33
|
+
const metadataPath = routes.metadata;
|
|
27
34
|
return async (req, res) => {
|
|
28
35
|
var _a;
|
|
29
36
|
// Parse the URL
|
|
@@ -49,12 +56,13 @@ function createMetadataHandler(app) {
|
|
|
49
56
|
sendJson({ error: { code, message } }, status);
|
|
50
57
|
};
|
|
51
58
|
// ---------------------------------------------------------
|
|
52
|
-
// 1. List Entries (GET
|
|
59
|
+
// 1. List Entries (GET {metadataPath}/:type)
|
|
53
60
|
// ---------------------------------------------------------
|
|
54
|
-
// Generic List:
|
|
55
|
-
// Also handles legacy
|
|
56
|
-
const
|
|
57
|
-
const
|
|
61
|
+
// Generic List: {metadataPath}/:type
|
|
62
|
+
// Also handles legacy {metadataPath} (defaults to objects)
|
|
63
|
+
const escapedPath = (0, utils_1.escapeRegexPath)(metadataPath);
|
|
64
|
+
const listMatch = url.match(new RegExp(`^${escapedPath}/([^/]+)$`));
|
|
65
|
+
const isRootMetadata = url === metadataPath;
|
|
58
66
|
if (method === 'GET' && (listMatch || isRootMetadata)) {
|
|
59
67
|
let type = isRootMetadata ? 'object' : listMatch[1];
|
|
60
68
|
if (type === 'objects')
|
|
@@ -78,9 +86,9 @@ function createMetadataHandler(app) {
|
|
|
78
86
|
});
|
|
79
87
|
}
|
|
80
88
|
// ---------------------------------------------------------
|
|
81
|
-
// 2. Get Single Entry (GET
|
|
89
|
+
// 2. Get Single Entry (GET {metadataPath}/:type/:id)
|
|
82
90
|
// ---------------------------------------------------------
|
|
83
|
-
const detailMatch = url.match(
|
|
91
|
+
const detailMatch = url.match(new RegExp(`^${escapedPath}/([^/]+)/([^/\\?]+)$`));
|
|
84
92
|
if (method === 'GET' && detailMatch) {
|
|
85
93
|
let [, type, id] = detailMatch;
|
|
86
94
|
if (type === 'objects')
|
|
@@ -89,7 +97,7 @@ function createMetadataHandler(app) {
|
|
|
89
97
|
if (type === 'object') {
|
|
90
98
|
const metadata = app.getObject(id);
|
|
91
99
|
if (!metadata) {
|
|
92
|
-
return sendError(
|
|
100
|
+
return sendError(types_2.ErrorCode.NOT_FOUND, `Object '${id}' not found`, 404);
|
|
93
101
|
}
|
|
94
102
|
// Convert fields to map with name populated
|
|
95
103
|
const fields = {};
|
|
@@ -110,13 +118,13 @@ function createMetadataHandler(app) {
|
|
|
110
118
|
// Generic Metadata (View, Form, etc.)
|
|
111
119
|
const content = app.metadata.get(type, id);
|
|
112
120
|
if (!content) {
|
|
113
|
-
return sendError(
|
|
121
|
+
return sendError(types_2.ErrorCode.NOT_FOUND, `${type} '${id}' not found`, 404);
|
|
114
122
|
}
|
|
115
123
|
return sendJson(content);
|
|
116
124
|
}
|
|
117
125
|
}
|
|
118
126
|
// ---------------------------------------------------------
|
|
119
|
-
// 3. Update Entry (POST/PUT
|
|
127
|
+
// 3. Update Entry (POST/PUT {metadataPath}/:type/:id)
|
|
120
128
|
// ---------------------------------------------------------
|
|
121
129
|
if ((method === 'POST' || method === 'PUT') && detailMatch) {
|
|
122
130
|
let [, type, id] = detailMatch;
|
|
@@ -126,27 +134,27 @@ function createMetadataHandler(app) {
|
|
|
126
134
|
try {
|
|
127
135
|
// await app.updateMetadata(type, id, body);
|
|
128
136
|
// return sendJson({ success: true });
|
|
129
|
-
return sendError(
|
|
137
|
+
return sendError(types_2.ErrorCode.INTERNAL_ERROR, 'Metadata updates via API are temporarily disabled in this architectural version.', 501);
|
|
130
138
|
}
|
|
131
139
|
catch (e) {
|
|
132
140
|
const isUserError = e.message.startsWith('Cannot update') || e.message.includes('not found');
|
|
133
|
-
return sendError(isUserError ?
|
|
141
|
+
return sendError(isUserError ? types_2.ErrorCode.INVALID_REQUEST : types_2.ErrorCode.INTERNAL_ERROR, e.message, isUserError ? 400 : 500);
|
|
134
142
|
}
|
|
135
143
|
}
|
|
136
144
|
// ---------------------------------------------------------
|
|
137
145
|
// 4. Object Sub-resources (Fields, Actions)
|
|
138
146
|
// ---------------------------------------------------------
|
|
139
|
-
// GET /
|
|
147
|
+
// GET {metadataPath}/object/:name/fields/:field
|
|
140
148
|
// Legacy path support.
|
|
141
|
-
const fieldMatch = url.match(
|
|
149
|
+
const fieldMatch = url.match(new RegExp(`^${escapedPath}/(?:objects|object)/([^/]+)/fields/([^/\\?]+)$`));
|
|
142
150
|
if (method === 'GET' && fieldMatch) {
|
|
143
151
|
const [, objectName, fieldName] = fieldMatch;
|
|
144
152
|
const metadata = app.getObject(objectName);
|
|
145
153
|
if (!metadata)
|
|
146
|
-
return sendError(
|
|
154
|
+
return sendError(types_2.ErrorCode.NOT_FOUND, `Object '${objectName}' not found`, 404);
|
|
147
155
|
const field = (_a = metadata.fields) === null || _a === void 0 ? void 0 : _a[fieldName];
|
|
148
156
|
if (!field)
|
|
149
|
-
return sendError(
|
|
157
|
+
return sendError(types_2.ErrorCode.NOT_FOUND, `Field '${fieldName}' not found`, 404);
|
|
150
158
|
return sendJson({
|
|
151
159
|
name: field.name || fieldName,
|
|
152
160
|
type: field.type,
|
|
@@ -162,13 +170,13 @@ function createMetadataHandler(app) {
|
|
|
162
170
|
regex: field.regex
|
|
163
171
|
});
|
|
164
172
|
}
|
|
165
|
-
// GET /
|
|
166
|
-
const actionsMatch = url.match(
|
|
173
|
+
// GET {metadataPath}/object/:name/actions
|
|
174
|
+
const actionsMatch = url.match(new RegExp(`^${escapedPath}/(?:objects|object)/([^/]+)/actions$`));
|
|
167
175
|
if (method === 'GET' && actionsMatch) {
|
|
168
176
|
const [, objectName] = actionsMatch;
|
|
169
177
|
const metadata = app.getObject(objectName);
|
|
170
178
|
if (!metadata)
|
|
171
|
-
return sendError(
|
|
179
|
+
return sendError(types_2.ErrorCode.NOT_FOUND, `Object '${objectName}' not found`, 404);
|
|
172
180
|
const actions = metadata.actions || {};
|
|
173
181
|
const formattedActions = Object.entries(actions).map(([key, action]) => {
|
|
174
182
|
const actionConfig = action;
|
|
@@ -184,14 +192,14 @@ function createMetadataHandler(app) {
|
|
|
184
192
|
return sendJson({ items: formattedActions });
|
|
185
193
|
}
|
|
186
194
|
// Not found
|
|
187
|
-
sendError(
|
|
195
|
+
sendError(types_2.ErrorCode.NOT_FOUND, 'Not Found', 404);
|
|
188
196
|
}
|
|
189
197
|
catch (e) {
|
|
190
198
|
console.error('[Metadata Handler] Error:', e);
|
|
191
199
|
res.statusCode = 500;
|
|
192
200
|
res.end(JSON.stringify({
|
|
193
201
|
error: {
|
|
194
|
-
code:
|
|
202
|
+
code: types_2.ErrorCode.INTERNAL_ERROR,
|
|
195
203
|
message: 'Internal Server Error'
|
|
196
204
|
}
|
|
197
205
|
}));
|
package/dist/metadata.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":";;AAoCA,sDAuMC;AA3OD,2CAA8E;AAE9E,mCAAoC;AACpC,mCAA0C;AAE1C,SAAS,QAAQ,CAAC,GAAoB;IAClC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACnC,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,IAAI,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QAClD,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACf,IAAI,CAAC,IAAI;gBAAE,OAAO,OAAO,CAAC,EAAE,CAAC,CAAC;YAC9B,IAAI,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YAC9B,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACT,MAAM,CAAC,CAAC,CAAC,CAAC;YACd,CAAC;QACL,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;AACP,CAAC;AAUD;;;;;;GAMG;AACH,SAAgB,qBAAqB,CAAC,GAAc,EAAE,OAAgC;IAClF,MAAM,MAAM,GAAG,IAAA,wBAAgB,EAAC,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,CAAC,CAAC;IACjD,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC;IACrC,OAAO,KAAK,EAAE,GAAoB,EAAE,GAAmB,EAAE,EAAE;;QACvD,gBAAgB;QAChB,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QAE1B,+BAA+B;QAC/B,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;QAClD,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,yBAAyB,CAAC,CAAC;QACzE,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,cAAc,CAAC,CAAC;QAE9D,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC3B,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;YACrB,GAAG,CAAC,GAAG,EAAE,CAAC;YACV,OAAO;QACX,CAAC;QAED,IAAI,CAAC;YACD,sBAAsB;YACtB,MAAM,QAAQ,GAAG,CAAC,IAAS,EAAE,MAAM,GAAG,GAAG,EAAE,EAAE;gBACzC,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;gBAClD,GAAG,CAAC,UAAU,GAAG,MAAM,CAAC;gBACxB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;YAClC,CAAC,CAAC;YAEF,MAAM,SAAS,GAAG,CAAC,IAAe,EAAE,OAAe,EAAE,MAAM,GAAG,GAAG,EAAE,EAAE;gBACjE,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;YACnD,CAAC,CAAC;YAEF,4DAA4D;YAC5D,6CAA6C;YAC7C,4DAA4D;YAE5D,qCAAqC;YACrC,2DAA2D;YAC3D,MAAM,WAAW,GAAG,IAAA,uBAAe,EAAC,YAAY,CAAC,CAAC;YAClD,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,WAAW,WAAW,CAAC,CAAC,CAAC;YACpE,MAAM,cAAc,GAAG,GAAG,KAAK,YAAY,CAAC;YAE5C,IAAI,MAAM,KAAK,KAAK,IAAI,CAAC,SAAS,IAAI,cAAc,CAAC,EAAE,CAAC;gBACpD,IAAI,IAAI,GAAG,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAU,CAAC,CAAC,CAAC,CAAC;gBACrD,IAAI,IAAI,KAAK,SAAS;oBAAE,IAAI,GAAG,QAAQ,CAAC,CAAC,iBAAiB;gBAE1D,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACpB,MAAM,OAAO,GAAG,GAAG,CAAC,UAAU,EAAE,CAAC;oBACjC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;wBAC/C,IAAI,EAAE,GAAG,CAAC,IAAI;wBACd,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI;wBAC5B,IAAI,EAAE,GAAG,CAAC,IAAI;wBACd,WAAW,EAAE,GAAG,CAAC,WAAW;wBAC5B,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE;qBAC3B,CAAC,CAAC,CAAC;oBACJ,wCAAwC;oBACxC,OAAO,QAAQ,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;gBACxC,CAAC;gBAED,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACxC,kCAAkC;gBAClC,OAAO,QAAQ,CAAC;oBACZ,KAAK,EAAE,OAAO;iBACjB,CAAC,CAAC;YACP,CAAC;YAED,4DAA4D;YAC5D,qDAAqD;YACrD,4DAA4D;YAE5D,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,WAAW,sBAAsB,CAAC,CAAC,CAAC;YAEjF,IAAI,MAAM,KAAK,KAAK,IAAI,WAAW,EAAE,CAAC;gBAClC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,WAAW,CAAC;gBAC/B,IAAI,IAAI,KAAK,SAAS;oBAAE,IAAI,GAAG,QAAQ,CAAC;gBAExC,iDAAiD;gBACjD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACpB,MAAM,QAAQ,GAAG,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;oBACnC,IAAI,CAAC,QAAQ,EAAE,CAAC;wBACZ,OAAO,SAAS,CAAC,iBAAS,CAAC,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,CAAC,CAAC;oBAC3E,CAAC;oBAED,4CAA4C;oBAC5C,MAAM,MAAM,GAAwB,EAAE,CAAC;oBACvC,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;wBAClB,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;4BACrD,MAAM,CAAC,GAAG,CAAC,GAAG;gCACV,GAAG,KAAK;gCACR,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,GAAG;6BAC1B,CAAC;wBACN,CAAC,CAAC,CAAC;oBACP,CAAC;oBAED,OAAO,QAAQ,CAAC;wBACZ,GAAG,QAAQ;wBACX,MAAM;qBACT,CAAC,CAAC;gBACP,CAAC;qBAAM,CAAC;oBACJ,sCAAsC;oBACtC,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;oBAC3C,IAAI,CAAC,OAAO,EAAE,CAAC;wBACV,OAAO,SAAS,CAAC,iBAAS,CAAC,SAAS,EAAE,GAAG,IAAI,KAAK,EAAE,aAAa,EAAE,GAAG,CAAC,CAAC;oBAC7E,CAAC;oBACD,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;gBAC7B,CAAC;YACL,CAAC;YAED,4DAA4D;YAC5D,sDAAsD;YACtD,4DAA4D;YAC5D,IAAI,CAAC,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC;gBACzD,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,WAAW,CAAC;gBAC/B,IAAI,IAAI,KAAK,SAAS;oBAAE,IAAI,GAAG,QAAQ,CAAC;gBAExC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;gBACjC,IAAI,CAAC;oBACD,4CAA4C;oBAC5C,sCAAsC;oBACtC,OAAO,SAAS,CAAC,iBAAS,CAAC,cAAc,EAAE,kFAAkF,EAAE,GAAG,CAAC,CAAC;gBACxI,CAAC;gBAAC,OAAO,CAAM,EAAE,CAAC;oBACd,MAAM,WAAW,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;oBAC7F,OAAO,SAAS,CACZ,WAAW,CAAC,CAAC,CAAC,iBAAS,CAAC,eAAe,CAAC,CAAC,CAAC,iBAAS,CAAC,cAAc,EAClE,CAAC,CAAC,OAAO,EACT,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAC1B,CAAC;gBACN,CAAC;YACL,CAAC;YAED,4DAA4D;YAC5D,4CAA4C;YAC5C,4DAA4D;YAE5D,gDAAgD;YAChD,uBAAuB;YACvB,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,WAAW,gDAAgD,CAAC,CAAC,CAAC;YAC1G,IAAI,MAAM,KAAK,KAAK,IAAI,UAAU,EAAE,CAAC;gBACjC,MAAM,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,GAAG,UAAU,CAAC;gBAC7C,MAAM,QAAQ,GAAG,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;gBAE3C,IAAI,CAAC,QAAQ;oBAAE,OAAO,SAAS,CAAC,iBAAS,CAAC,SAAS,EAAE,WAAW,UAAU,aAAa,EAAE,GAAG,CAAC,CAAC;gBAE9F,MAAM,KAAK,GAAG,MAAA,QAAQ,CAAC,MAAM,0CAAG,SAAS,CAAC,CAAC;gBAC3C,IAAI,CAAC,KAAK;oBAAE,OAAO,SAAS,CAAC,iBAAS,CAAC,SAAS,EAAE,UAAU,SAAS,aAAa,EAAE,GAAG,CAAC,CAAC;gBAEzF,OAAO,QAAQ,CAAC;oBACZ,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,SAAS;oBAC7B,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;oBACxB,MAAM,EAAE,KAAK,CAAC,MAAM;oBACpB,YAAY,EAAE,KAAK,CAAC,YAAY;oBAChC,OAAO,EAAE,KAAK,CAAC,OAAO;oBACtB,GAAG,EAAE,KAAK,CAAC,GAAG;oBACd,GAAG,EAAE,KAAK,CAAC,GAAG;oBACd,UAAU,EAAE,KAAK,CAAC,UAAU;oBAC5B,UAAU,EAAE,KAAK,CAAC,UAAU;oBAC5B,KAAK,EAAE,KAAK,CAAC,KAAK;iBACrB,CAAC,CAAC;YACP,CAAC;YAED,0CAA0C;YAC1C,MAAM,YAAY,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,WAAW,sCAAsC,CAAC,CAAC,CAAC;YAClG,IAAI,MAAM,KAAK,KAAK,IAAI,YAAY,EAAE,CAAC;gBACnC,MAAM,CAAC,EAAE,UAAU,CAAC,GAAG,YAAY,CAAC;gBACpC,MAAM,QAAQ,GAAG,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;gBAE3C,IAAI,CAAC,QAAQ;oBAAE,OAAO,SAAS,CAAC,iBAAS,CAAC,SAAS,EAAE,WAAW,UAAU,aAAa,EAAE,GAAG,CAAC,CAAC;gBAE9F,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,IAAI,EAAE,CAAC;gBACvC,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE;oBACnE,MAAM,YAAY,GAAG,MAAa,CAAC;oBACnC,MAAM,SAAS,GAAG,CAAC,CAAC,YAAY,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;oBACvF,OAAO;wBACH,IAAI,EAAE,GAAG;wBACT,IAAI,EAAE,YAAY,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;wBAC5D,KAAK,EAAE,YAAY,CAAC,KAAK,IAAI,GAAG;wBAChC,MAAM,EAAE,YAAY,CAAC,MAAM,IAAI,EAAE;wBACjC,WAAW,EAAE,YAAY,CAAC,WAAW;qBACxC,CAAC;gBACN,CAAC,CAAC,CAAC;gBAEH,OAAO,QAAQ,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;YACjD,CAAC;YAED,YAAY;YACZ,SAAS,CAAC,iBAAS,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC;QAErD,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YACd,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,CAAC,CAAC,CAAC;YAC9C,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;YACrB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE;oBACH,IAAI,EAAE,iBAAS,CAAC,cAAc;oBAC9B,OAAO,EAAE,uBAAuB;iBACnC;aACJ,CAAC,CAAC,CAAC;QACR,CAAC;IACL,CAAC,CAAC;AACN,CAAC"}
|
package/dist/openapi.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IObjectQL } from '@objectql/types';
|
|
1
|
+
import { IObjectQL, ApiRouteConfig } from '@objectql/types';
|
|
2
2
|
interface OpenAPISchema {
|
|
3
3
|
openapi: string;
|
|
4
4
|
info: {
|
|
@@ -10,5 +10,5 @@ interface OpenAPISchema {
|
|
|
10
10
|
schemas: Record<string, any>;
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
-
export declare function generateOpenAPI(app: IObjectQL): OpenAPISchema;
|
|
13
|
+
export declare function generateOpenAPI(app: IObjectQL, routeConfig?: ApiRouteConfig): OpenAPISchema;
|
|
14
14
|
export {};
|
package/dist/openapi.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateOpenAPI = generateOpenAPI;
|
|
4
|
-
|
|
4
|
+
const types_1 = require("@objectql/types");
|
|
5
|
+
function generateOpenAPI(app, routeConfig) {
|
|
5
6
|
const registry = app.metadata; // Direct access or via interface
|
|
6
7
|
const objects = registry.list('object');
|
|
8
|
+
const routes = (0, types_1.resolveApiRoutes)(routeConfig);
|
|
7
9
|
const paths = {};
|
|
8
10
|
const schemas = {};
|
|
9
11
|
// 1. JSON-RPC Endpoint
|
|
10
|
-
paths[
|
|
12
|
+
paths[routes.rpc] = {
|
|
11
13
|
post: {
|
|
12
14
|
summary: 'JSON-RPC Entry Point',
|
|
13
15
|
description: 'Execute any ObjectQL operation via a JSON body.',
|
|
@@ -54,8 +56,8 @@ function generateOpenAPI(app) {
|
|
|
54
56
|
// 3. REST API Paths
|
|
55
57
|
for (const obj of objects) {
|
|
56
58
|
const name = obj.name;
|
|
57
|
-
const basePath =
|
|
58
|
-
// GET
|
|
59
|
+
const basePath = `${routes.data}/${name}`; // Standard REST Path
|
|
60
|
+
// GET {dataPath}/:name (List)
|
|
59
61
|
paths[basePath] = {
|
|
60
62
|
get: {
|
|
61
63
|
summary: `List ${name}`,
|
package/dist/openapi.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":";;AAcA,
|
|
1
|
+
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for server operations
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Escapes special regex characters in a path string for use in RegExp
|
|
6
|
+
* @param path - The path string to escape
|
|
7
|
+
* @returns Escaped path string safe for use in RegExp
|
|
8
|
+
*/
|
|
9
|
+
export declare function escapeRegexPath(path: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Normalizes a path to ensure it starts with a forward slash
|
|
12
|
+
* @param path - The path string to normalize
|
|
13
|
+
* @returns Normalized path string starting with '/'
|
|
14
|
+
*/
|
|
15
|
+
export declare function normalizePath(path: string): string;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Utility functions for server operations
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.escapeRegexPath = escapeRegexPath;
|
|
7
|
+
exports.normalizePath = normalizePath;
|
|
8
|
+
/**
|
|
9
|
+
* Escapes special regex characters in a path string for use in RegExp
|
|
10
|
+
* @param path - The path string to escape
|
|
11
|
+
* @returns Escaped path string safe for use in RegExp
|
|
12
|
+
*/
|
|
13
|
+
function escapeRegexPath(path) {
|
|
14
|
+
return path.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Normalizes a path to ensure it starts with a forward slash
|
|
18
|
+
* @param path - The path string to normalize
|
|
19
|
+
* @returns Normalized path string starting with '/'
|
|
20
|
+
*/
|
|
21
|
+
function normalizePath(path) {
|
|
22
|
+
return path.startsWith('/') ? path : `/${path}`;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAOH,0CAEC;AAOD,sCAEC;AAhBD;;;;GAIG;AACH,SAAgB,eAAe,CAAC,IAAY;IACxC,OAAO,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACvD,CAAC;AAED;;;;GAIG;AACH,SAAgB,aAAa,CAAC,IAAY;IACtC,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;AACpD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectql/server",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.4",
|
|
4
4
|
"description": "HTTP server adapter for ObjectQL - Express/NestJS compatible with GraphQL and REST API support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"objectql",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"graphql": "^16.8.1",
|
|
22
22
|
"@graphql-tools/schema": "^10.0.2",
|
|
23
23
|
"js-yaml": "^4.1.1",
|
|
24
|
-
"@objectql/core": "1.8.
|
|
25
|
-
"@objectql/types": "1.8.
|
|
24
|
+
"@objectql/core": "1.8.4",
|
|
25
|
+
"@objectql/types": "1.8.4"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/js-yaml": "^4.0.9",
|
package/src/adapters/node.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { IObjectQL } from '@objectql/types';
|
|
1
|
+
import { IObjectQL, ApiRouteConfig, resolveApiRoutes } from '@objectql/types';
|
|
2
2
|
import { ObjectQLServer } from '../server';
|
|
3
3
|
import { ObjectQLRequest, ErrorCode, IFileStorage } from '../types';
|
|
4
4
|
import { IncomingMessage, ServerResponse } from 'http';
|
|
5
5
|
import { generateOpenAPI } from '../openapi';
|
|
6
6
|
import { createFileUploadHandler, createBatchFileUploadHandler, createFileDownloadHandler } from '../file-handler';
|
|
7
7
|
import { LocalFileStorage } from '../storage';
|
|
8
|
+
import { escapeRegexPath } from '../utils';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Options for createNodeHandler
|
|
@@ -12,6 +13,8 @@ import { LocalFileStorage } from '../storage';
|
|
|
12
13
|
export interface NodeHandlerOptions {
|
|
13
14
|
/** File storage provider (defaults to LocalFileStorage) */
|
|
14
15
|
fileStorage?: IFileStorage;
|
|
16
|
+
/** Custom API route configuration */
|
|
17
|
+
routes?: ApiRouteConfig;
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
/**
|
|
@@ -19,11 +22,13 @@ export interface NodeHandlerOptions {
|
|
|
19
22
|
*/
|
|
20
23
|
export function createNodeHandler(app: IObjectQL, options?: NodeHandlerOptions) {
|
|
21
24
|
const server = new ObjectQLServer(app);
|
|
25
|
+
const routes = resolveApiRoutes(options?.routes);
|
|
22
26
|
|
|
23
27
|
// Initialize file storage
|
|
28
|
+
const defaultBaseUrl = process.env.OBJECTQL_BASE_URL || `http://localhost:3000${routes.files}`;
|
|
24
29
|
const fileStorage = options?.fileStorage || new LocalFileStorage({
|
|
25
30
|
baseDir: process.env.OBJECTQL_UPLOAD_DIR || './uploads',
|
|
26
|
-
baseUrl:
|
|
31
|
+
baseUrl: defaultBaseUrl
|
|
27
32
|
});
|
|
28
33
|
|
|
29
34
|
// Create file handlers
|
|
@@ -103,17 +108,18 @@ export function createNodeHandler(app: IObjectQL, options?: NodeHandlerOptions)
|
|
|
103
108
|
const pathName = urlObj.pathname;
|
|
104
109
|
const method = req.method;
|
|
105
110
|
|
|
106
|
-
// 1. JSON-RPC: POST
|
|
107
|
-
if (pathName ===
|
|
111
|
+
// 1. JSON-RPC: POST {rpcPath}
|
|
112
|
+
if (pathName === routes.rpc && method === 'POST') {
|
|
108
113
|
await processBody(req, async (json) => {
|
|
109
114
|
await handleRequest(json);
|
|
110
115
|
}, res);
|
|
111
116
|
return;
|
|
112
117
|
}
|
|
113
118
|
|
|
114
|
-
// 2. REST API:
|
|
115
|
-
// Regex to match /
|
|
116
|
-
const
|
|
119
|
+
// 2. REST API: {dataPath}/:object and {dataPath}/:object/:id
|
|
120
|
+
// Regex to match {dataPath}/objectName(/id)?
|
|
121
|
+
const escapedDataPath = escapeRegexPath(routes.data);
|
|
122
|
+
const restMatch = pathName.match(new RegExp(`^${escapedDataPath}/([^/]+)(?:/(.+))?$`));
|
|
117
123
|
|
|
118
124
|
if (restMatch) {
|
|
119
125
|
const objectName = restMatch[1];
|
|
@@ -121,7 +127,7 @@ export function createNodeHandler(app: IObjectQL, options?: NodeHandlerOptions)
|
|
|
121
127
|
const query = Object.fromEntries(urlObj.searchParams.entries());
|
|
122
128
|
|
|
123
129
|
if (method === 'GET') {
|
|
124
|
-
// GET
|
|
130
|
+
// GET {dataPath}/:object/:id -> findOne
|
|
125
131
|
if (id) {
|
|
126
132
|
await handleRequest({
|
|
127
133
|
op: 'findOne',
|
|
@@ -129,7 +135,7 @@ export function createNodeHandler(app: IObjectQL, options?: NodeHandlerOptions)
|
|
|
129
135
|
args: id
|
|
130
136
|
});
|
|
131
137
|
}
|
|
132
|
-
// GET
|
|
138
|
+
// GET {dataPath}/:object -> find (List)
|
|
133
139
|
else {
|
|
134
140
|
// Parse standard params
|
|
135
141
|
const args: any = {};
|
|
@@ -149,7 +155,7 @@ export function createNodeHandler(app: IObjectQL, options?: NodeHandlerOptions)
|
|
|
149
155
|
}
|
|
150
156
|
|
|
151
157
|
if (method === 'POST' && !id) {
|
|
152
|
-
// POST
|
|
158
|
+
// POST {dataPath}/:object -> create
|
|
153
159
|
await processBody(req, async (body) => {
|
|
154
160
|
await handleRequest({
|
|
155
161
|
op: 'create',
|
|
@@ -161,7 +167,7 @@ export function createNodeHandler(app: IObjectQL, options?: NodeHandlerOptions)
|
|
|
161
167
|
}
|
|
162
168
|
|
|
163
169
|
if (method === 'PATCH' && id) {
|
|
164
|
-
// PATCH
|
|
170
|
+
// PATCH {dataPath}/:object/:id -> update
|
|
165
171
|
await processBody(req, async (body) => {
|
|
166
172
|
await handleRequest({
|
|
167
173
|
op: 'update',
|
|
@@ -176,7 +182,7 @@ export function createNodeHandler(app: IObjectQL, options?: NodeHandlerOptions)
|
|
|
176
182
|
}
|
|
177
183
|
|
|
178
184
|
if (method === 'DELETE' && id) {
|
|
179
|
-
// DELETE
|
|
185
|
+
// DELETE {dataPath}/:object/:id -> delete
|
|
180
186
|
await handleRequest({
|
|
181
187
|
op: 'delete',
|
|
182
188
|
object: objectName,
|
|
@@ -187,20 +193,21 @@ export function createNodeHandler(app: IObjectQL, options?: NodeHandlerOptions)
|
|
|
187
193
|
}
|
|
188
194
|
|
|
189
195
|
// File Upload Endpoints
|
|
190
|
-
// POST /
|
|
191
|
-
if (pathName ===
|
|
196
|
+
// POST {filesPath}/upload - Single file upload
|
|
197
|
+
if (pathName === `${routes.files}/upload` && method === 'POST') {
|
|
192
198
|
await uploadHandler(req, res);
|
|
193
199
|
return;
|
|
194
200
|
}
|
|
195
201
|
|
|
196
|
-
// POST /
|
|
197
|
-
if (pathName ===
|
|
202
|
+
// POST {filesPath}/upload/batch - Batch file upload
|
|
203
|
+
if (pathName === `${routes.files}/upload/batch` && method === 'POST') {
|
|
198
204
|
await batchUploadHandler(req, res);
|
|
199
205
|
return;
|
|
200
206
|
}
|
|
201
207
|
|
|
202
|
-
// GET
|
|
203
|
-
const
|
|
208
|
+
// GET {filesPath}/:fileId - Download file
|
|
209
|
+
const escapedFilesPath = escapeRegexPath(routes.files);
|
|
210
|
+
const fileMatch = pathName.match(new RegExp(`^${escapedFilesPath}/([^/]+)$`));
|
|
204
211
|
if (fileMatch && method === 'GET') {
|
|
205
212
|
const fileId = fileMatch[1];
|
|
206
213
|
await downloadHandler(req, res, fileId);
|
|
@@ -209,7 +216,7 @@ export function createNodeHandler(app: IObjectQL, options?: NodeHandlerOptions)
|
|
|
209
216
|
|
|
210
217
|
// Fallback or 404
|
|
211
218
|
if (req.method === 'POST') {
|
|
212
|
-
// Fallback for root POSTs if people forget
|
|
219
|
+
// Fallback for root POSTs if people forget {rpcPath} but send to /api something
|
|
213
220
|
await processBody(req, handleRequest, res);
|
|
214
221
|
return;
|
|
215
222
|
}
|
package/src/adapters/rest.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { IObjectQL } from '@objectql/types';
|
|
1
|
+
import { IObjectQL, ApiRouteConfig, resolveApiRoutes } from '@objectql/types';
|
|
2
2
|
import { ObjectQLServer } from '../server';
|
|
3
3
|
import { ObjectQLRequest, ErrorCode } from '../types';
|
|
4
4
|
import { IncomingMessage, ServerResponse } from 'http';
|
|
5
|
+
import { escapeRegexPath } from '../utils';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Parse query string parameters
|
|
@@ -60,20 +61,33 @@ function sendJSON(res: ServerResponse, statusCode: number, data: any) {
|
|
|
60
61
|
res.end(JSON.stringify(data));
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Options for createRESTHandler
|
|
66
|
+
*/
|
|
67
|
+
export interface RESTHandlerOptions {
|
|
68
|
+
/** Custom API route configuration */
|
|
69
|
+
routes?: ApiRouteConfig;
|
|
70
|
+
}
|
|
71
|
+
|
|
63
72
|
/**
|
|
64
73
|
* Creates a REST-style HTTP request handler for ObjectQL
|
|
65
74
|
*
|
|
66
|
-
* Endpoints:
|
|
67
|
-
* - GET
|
|
68
|
-
* - GET
|
|
69
|
-
* - POST
|
|
70
|
-
* - POST
|
|
71
|
-
* - POST
|
|
72
|
-
* - PUT
|
|
73
|
-
* - DELETE
|
|
75
|
+
* Default Endpoints (configurable via routes option):
|
|
76
|
+
* - GET {dataPath}/:object - List records
|
|
77
|
+
* - GET {dataPath}/:object/:id - Get single record
|
|
78
|
+
* - POST {dataPath}/:object - Create record (or create many if array)
|
|
79
|
+
* - POST {dataPath}/:object/bulk-update - Update many records
|
|
80
|
+
* - POST {dataPath}/:object/bulk-delete - Delete many records
|
|
81
|
+
* - PUT {dataPath}/:object/:id - Update record
|
|
82
|
+
* - DELETE {dataPath}/:object/:id - Delete record
|
|
83
|
+
*
|
|
84
|
+
* @param app - ObjectQL application instance
|
|
85
|
+
* @param options - Optional configuration including custom routes
|
|
74
86
|
*/
|
|
75
|
-
export function createRESTHandler(app: IObjectQL) {
|
|
87
|
+
export function createRESTHandler(app: IObjectQL, options?: RESTHandlerOptions) {
|
|
76
88
|
const server = new ObjectQLServer(app);
|
|
89
|
+
const routes = resolveApiRoutes(options?.routes);
|
|
90
|
+
const dataPath = routes.data;
|
|
77
91
|
|
|
78
92
|
return async (req: IncomingMessage & { body?: any }, res: ServerResponse) => {
|
|
79
93
|
try {
|
|
@@ -101,8 +115,9 @@ export function createRESTHandler(app: IObjectQL) {
|
|
|
101
115
|
const url = req.url || '';
|
|
102
116
|
const method = req.method || 'GET';
|
|
103
117
|
|
|
104
|
-
// Parse URL:
|
|
105
|
-
const
|
|
118
|
+
// Parse URL: {dataPath}/:object or {dataPath}/:object/:id or {dataPath}/:object/bulk-*
|
|
119
|
+
const escapedPath = escapeRegexPath(dataPath);
|
|
120
|
+
const match = url.match(new RegExp(`^${escapedPath}/([^/\\?]+)(?:/([^/\\?]+))?(\\?.*)?$`));
|
|
106
121
|
|
|
107
122
|
if (!match) {
|
|
108
123
|
sendJSON(res, 404, {
|
|
@@ -129,7 +144,7 @@ export function createRESTHandler(app: IObjectQL) {
|
|
|
129
144
|
args: id
|
|
130
145
|
};
|
|
131
146
|
} else {
|
|
132
|
-
// GET
|
|
147
|
+
// GET {dataPath}/:object - List records
|
|
133
148
|
const args: any = {};
|
|
134
149
|
|
|
135
150
|
// Parse query parameters
|
|
@@ -167,7 +182,7 @@ export function createRESTHandler(app: IObjectQL) {
|
|
|
167
182
|
|
|
168
183
|
// Check for bulk operations
|
|
169
184
|
if (id === 'bulk-update') {
|
|
170
|
-
// POST
|
|
185
|
+
// POST {dataPath}/:object/bulk-update - Update many records
|
|
171
186
|
qlRequest = {
|
|
172
187
|
op: 'updateMany',
|
|
173
188
|
object: objectName,
|
|
@@ -177,7 +192,7 @@ export function createRESTHandler(app: IObjectQL) {
|
|
|
177
192
|
}
|
|
178
193
|
};
|
|
179
194
|
} else if (id === 'bulk-delete') {
|
|
180
|
-
// POST
|
|
195
|
+
// POST {dataPath}/:object/bulk-delete - Delete many records
|
|
181
196
|
qlRequest = {
|
|
182
197
|
op: 'deleteMany',
|
|
183
198
|
object: objectName,
|
|
@@ -186,14 +201,14 @@ export function createRESTHandler(app: IObjectQL) {
|
|
|
186
201
|
}
|
|
187
202
|
};
|
|
188
203
|
} else if (Array.isArray(createBody)) {
|
|
189
|
-
// POST
|
|
204
|
+
// POST {dataPath}/:object with array - Create many records
|
|
190
205
|
qlRequest = {
|
|
191
206
|
op: 'createMany',
|
|
192
207
|
object: objectName,
|
|
193
208
|
args: createBody
|
|
194
209
|
};
|
|
195
210
|
} else {
|
|
196
|
-
// POST
|
|
211
|
+
// POST {dataPath}/:object - Create single record
|
|
197
212
|
qlRequest = {
|
|
198
213
|
op: 'create',
|
|
199
214
|
object: objectName,
|
|
@@ -204,7 +219,7 @@ export function createRESTHandler(app: IObjectQL) {
|
|
|
204
219
|
|
|
205
220
|
case 'PUT':
|
|
206
221
|
case 'PATCH':
|
|
207
|
-
// PUT
|
|
222
|
+
// PUT {dataPath}/:object/:id - Update record
|
|
208
223
|
if (!id) {
|
|
209
224
|
sendJSON(res, 400, {
|
|
210
225
|
error: {
|
|
@@ -227,7 +242,7 @@ export function createRESTHandler(app: IObjectQL) {
|
|
|
227
242
|
break;
|
|
228
243
|
|
|
229
244
|
case 'DELETE':
|
|
230
|
-
// DELETE
|
|
245
|
+
// DELETE {dataPath}/:object/:id - Delete record
|
|
231
246
|
if (!id) {
|
|
232
247
|
sendJSON(res, 400, {
|
|
233
248
|
error: {
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export * from './types';
|
|
2
|
+
export * from './utils';
|
|
2
3
|
export * from './openapi';
|
|
3
4
|
export * from './server';
|
|
4
5
|
export * from './metadata';
|
|
5
|
-
export * from './studio';
|
|
6
6
|
export * from './storage';
|
|
7
7
|
export * from './file-handler';
|
|
8
8
|
// We export createNodeHandler from root for convenience,
|