@objectql/server 1.4.0 → 1.5.0
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 +12 -0
- package/dist/adapters/node.js +116 -42
- package/dist/adapters/node.js.map +1 -1
- package/dist/metadata.d.ts +1 -1
- package/dist/metadata.js +101 -115
- package/dist/metadata.js.map +1 -1
- package/dist/openapi.js +73 -24
- package/dist/openapi.js.map +1 -1
- package/package.json +3 -3
- package/src/adapters/node.ts +124 -45
- package/src/metadata.ts +112 -125
- package/src/openapi.ts +80 -32
- package/tsconfig.json +5 -1
- package/tsconfig.tsbuildinfo +1 -1
package/dist/openapi.js
CHANGED
|
@@ -6,7 +6,40 @@ function generateOpenAPI(app) {
|
|
|
6
6
|
const objects = registry.list('object');
|
|
7
7
|
const paths = {};
|
|
8
8
|
const schemas = {};
|
|
9
|
-
// 1.
|
|
9
|
+
// 1. JSON-RPC Endpoint
|
|
10
|
+
paths['/api/objectql'] = {
|
|
11
|
+
post: {
|
|
12
|
+
summary: 'JSON-RPC Entry Point',
|
|
13
|
+
description: 'Execute any ObjectQL operation via a JSON body.',
|
|
14
|
+
tags: ['System'],
|
|
15
|
+
requestBody: {
|
|
16
|
+
content: {
|
|
17
|
+
'application/json': {
|
|
18
|
+
schema: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
properties: {
|
|
21
|
+
op: { type: 'string', enum: ['find', 'findOne', 'create', 'update', 'delete', 'count', 'action'] },
|
|
22
|
+
object: { type: 'string' },
|
|
23
|
+
args: { type: 'object' }
|
|
24
|
+
},
|
|
25
|
+
required: ['op', 'object']
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
responses: {
|
|
31
|
+
200: {
|
|
32
|
+
description: 'Operation Result',
|
|
33
|
+
content: {
|
|
34
|
+
'application/json': {
|
|
35
|
+
schema: { type: 'object' } // Dynamic result
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
// 2. Generate Schemas
|
|
10
43
|
for (const obj of objects) {
|
|
11
44
|
const schemaName = obj.name;
|
|
12
45
|
const properties = {};
|
|
@@ -17,29 +50,21 @@ function generateOpenAPI(app) {
|
|
|
17
50
|
type: 'object',
|
|
18
51
|
properties
|
|
19
52
|
};
|
|
20
|
-
// 2. Generate Paths (RPC Style representation for documentation purposes)
|
|
21
|
-
// Since we only have one endpoint, we might document operations as descriptions
|
|
22
|
-
// Or if we support REST style in the future, we would add /object paths here.
|
|
23
|
-
// For now, let's document the "Virtual" REST API that could exist via a gateway
|
|
24
|
-
// OR just document the schema.
|
|
25
|
-
// Let's assume the user might want to see standard CRUD paths even if implementation is RPC,
|
|
26
|
-
// so they can pass it to frontend generators?
|
|
27
|
-
// No, that would be misleading if the server doesn't support it.
|
|
28
|
-
// Let's DOCUMENT the RPC operations as if they were paths,
|
|
29
|
-
// OR clearer: One path /api/objectql with polymorphic body?
|
|
30
|
-
// Swagger UI handles oneOf poorly for top level operations sometimes.
|
|
31
53
|
}
|
|
32
|
-
//
|
|
33
|
-
// Assuming we WILL support REST mapping in this update.
|
|
54
|
+
// 3. REST API Paths
|
|
34
55
|
for (const obj of objects) {
|
|
35
56
|
const name = obj.name;
|
|
36
|
-
|
|
37
|
-
|
|
57
|
+
const basePath = `/api/data/${name}`; // Standard REST Path
|
|
58
|
+
// GET /api/data/:name (List)
|
|
59
|
+
paths[basePath] = {
|
|
38
60
|
get: {
|
|
39
|
-
summary: `List ${name}
|
|
61
|
+
summary: `List ${name}`,
|
|
40
62
|
tags: [name],
|
|
41
63
|
parameters: [
|
|
42
|
-
{ name: 'filter', in: 'query', schema: { type: 'string' }, description: 'JSON filter args' }
|
|
64
|
+
{ name: 'filter', in: 'query', schema: { type: 'string' }, description: 'JSON filter args' },
|
|
65
|
+
{ name: 'fields', in: 'query', schema: { type: 'string' }, description: 'Comma-separated fields to return' },
|
|
66
|
+
{ name: 'top', in: 'query', schema: { type: 'integer' }, description: 'Limit' },
|
|
67
|
+
{ name: 'skip', in: 'query', schema: { type: 'integer' }, description: 'Offset' }
|
|
43
68
|
],
|
|
44
69
|
responses: {
|
|
45
70
|
200: {
|
|
@@ -77,26 +102,50 @@ function generateOpenAPI(app) {
|
|
|
77
102
|
}
|
|
78
103
|
}
|
|
79
104
|
};
|
|
80
|
-
//
|
|
81
|
-
paths[
|
|
105
|
+
// /api/data/:name/:id
|
|
106
|
+
paths[`${basePath}/{id}`] = {
|
|
82
107
|
get: {
|
|
83
108
|
summary: `Get ${name}`,
|
|
84
109
|
tags: [name],
|
|
85
110
|
parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],
|
|
86
|
-
responses: {
|
|
111
|
+
responses: {
|
|
112
|
+
200: {
|
|
113
|
+
description: 'Item',
|
|
114
|
+
content: {
|
|
115
|
+
'application/json': {
|
|
116
|
+
schema: { $ref: `#/components/schemas/${name}` }
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
87
121
|
},
|
|
88
122
|
patch: {
|
|
89
123
|
summary: `Update ${name}`,
|
|
90
124
|
tags: [name],
|
|
91
125
|
parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],
|
|
92
|
-
requestBody: {
|
|
93
|
-
|
|
126
|
+
requestBody: {
|
|
127
|
+
content: {
|
|
128
|
+
'application/json': {
|
|
129
|
+
schema: {
|
|
130
|
+
type: 'object',
|
|
131
|
+
properties: {
|
|
132
|
+
data: { type: 'object' }
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
responses: {
|
|
139
|
+
200: { description: 'Updated' }
|
|
140
|
+
}
|
|
94
141
|
},
|
|
95
142
|
delete: {
|
|
96
143
|
summary: `Delete ${name}`,
|
|
97
144
|
tags: [name],
|
|
98
145
|
parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],
|
|
99
|
-
responses: {
|
|
146
|
+
responses: {
|
|
147
|
+
200: { description: 'Deleted' }
|
|
148
|
+
}
|
|
100
149
|
}
|
|
101
150
|
};
|
|
102
151
|
}
|
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/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectql/server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "HTTP Server Adapter for ObjectQL",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"js-yaml": "^4.1.1",
|
|
9
|
-
"@objectql/
|
|
10
|
-
"@objectql/
|
|
9
|
+
"@objectql/core": "1.5.0",
|
|
10
|
+
"@objectql/types": "1.5.0"
|
|
11
11
|
},
|
|
12
12
|
"devDependencies": {
|
|
13
13
|
"@types/js-yaml": "^4.0.9",
|
package/src/adapters/node.ts
CHANGED
|
@@ -23,13 +23,13 @@ export function createNodeHandler(app: IObjectQL) {
|
|
|
23
23
|
|
|
24
24
|
const handleRequest = async (json: any) => {
|
|
25
25
|
try {
|
|
26
|
-
//
|
|
26
|
+
// Determine Operation based on JSON or previously derived info
|
|
27
27
|
const qlReq: ObjectQLRequest = {
|
|
28
28
|
op: json.op,
|
|
29
29
|
object: json.object,
|
|
30
30
|
args: json.args,
|
|
31
|
-
user: json.user,
|
|
32
|
-
ai_context: json.ai_context
|
|
31
|
+
user: json.user,
|
|
32
|
+
ai_context: json.ai_context
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
const result = await server.handle(qlReq);
|
|
@@ -66,6 +66,7 @@ export function createNodeHandler(app: IObjectQL) {
|
|
|
66
66
|
res.statusCode = statusCode;
|
|
67
67
|
res.end(JSON.stringify(result));
|
|
68
68
|
} catch (e) {
|
|
69
|
+
console.error(e);
|
|
69
70
|
res.statusCode = 500;
|
|
70
71
|
res.end(JSON.stringify({
|
|
71
72
|
error: {
|
|
@@ -76,56 +77,134 @@ export function createNodeHandler(app: IObjectQL) {
|
|
|
76
77
|
}
|
|
77
78
|
};
|
|
78
79
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
80
|
+
// Parse URL
|
|
81
|
+
const urlObj = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
|
|
82
|
+
const pathName = urlObj.pathname;
|
|
83
|
+
const method = req.method;
|
|
84
|
+
|
|
85
|
+
// 1. JSON-RPC: POST /api/objectql
|
|
86
|
+
if (pathName === '/api/objectql' && method === 'POST') {
|
|
87
|
+
await processBody(req, async (json) => {
|
|
88
|
+
await handleRequest(json);
|
|
89
|
+
}, res);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 2. REST API: /api/data/:object and /api/data/:object/:id
|
|
94
|
+
// Regex to match /api/data/objectName(/id)?
|
|
95
|
+
const restMatch = pathName.match(/^\/api\/data\/([^/]+)(?:\/(.+))?$/);
|
|
96
|
+
|
|
97
|
+
if (restMatch) {
|
|
98
|
+
const objectName = restMatch[1];
|
|
99
|
+
const id = restMatch[2];
|
|
100
|
+
const query = Object.fromEntries(urlObj.searchParams.entries());
|
|
101
|
+
|
|
102
|
+
if (method === 'GET') {
|
|
103
|
+
// GET /api/data/:object/:id -> findOne
|
|
104
|
+
if (id) {
|
|
105
|
+
await handleRequest({
|
|
106
|
+
op: 'findOne',
|
|
90
107
|
object: objectName,
|
|
91
|
-
args: {} //
|
|
92
|
-
|
|
93
|
-
|
|
108
|
+
args: { filters: [['_id', '=', id]] } // Assuming _id or id mapping
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
// GET /api/data/:object -> find (List)
|
|
112
|
+
else {
|
|
113
|
+
// Parse standard params
|
|
114
|
+
const args: any = {};
|
|
115
|
+
if (query.fields) args.fields = (query.fields as string).split(',');
|
|
116
|
+
if (query.top) args.limit = parseInt(query.top as string);
|
|
117
|
+
if (query.skip) args.skip = parseInt(query.skip as string);
|
|
118
|
+
if (query.filter) {
|
|
119
|
+
try {
|
|
120
|
+
args.filters = JSON.parse(query.filter as string);
|
|
121
|
+
} catch (e) {
|
|
122
|
+
// ignore invalid filter json
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
await handleRequest({ op: 'find', object: objectName, args });
|
|
94
126
|
}
|
|
127
|
+
return;
|
|
95
128
|
}
|
|
96
129
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
130
|
+
if (method === 'POST' && !id) {
|
|
131
|
+
// POST /api/data/:object -> create
|
|
132
|
+
await processBody(req, async (body) => {
|
|
133
|
+
await handleRequest({
|
|
134
|
+
op: 'create',
|
|
135
|
+
object: objectName,
|
|
136
|
+
args: body.data || body // Support enclosed in data or flat
|
|
137
|
+
});
|
|
138
|
+
}, res);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (method === 'PATCH' && id) {
|
|
143
|
+
// PATCH /api/data/:object/:id -> update
|
|
144
|
+
await processBody(req, async (body) => {
|
|
145
|
+
await handleRequest({
|
|
146
|
+
op: 'update',
|
|
147
|
+
object: objectName,
|
|
148
|
+
args: {
|
|
149
|
+
id: id,
|
|
150
|
+
data: body.data || body
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}, res);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (method === 'DELETE' && id) {
|
|
158
|
+
// DELETE /api/data/:object/:id -> delete
|
|
159
|
+
await handleRequest({
|
|
160
|
+
op: 'delete',
|
|
161
|
+
object: objectName,
|
|
162
|
+
args: { id: id }
|
|
163
|
+
});
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Fallback or 404
|
|
169
|
+
if (req.method === 'POST') {
|
|
170
|
+
// Fallback for root POSTs if people forget /api/objectql but send to /api something
|
|
171
|
+
await processBody(req, handleRequest, res);
|
|
172
|
+
return;
|
|
105
173
|
}
|
|
106
174
|
|
|
107
|
-
//
|
|
108
|
-
if (
|
|
109
|
-
|
|
175
|
+
// Special case for root: since we accept POST / (RPC), correct response for GET / is 405
|
|
176
|
+
if (pathName === '/') {
|
|
177
|
+
res.setHeader('Allow', 'POST');
|
|
178
|
+
res.statusCode = 405;
|
|
179
|
+
res.end(JSON.stringify({ error: { code: ErrorCode.INVALID_REQUEST, message: 'Method Not Allowed. Use POST for JSON-RPC.' } }));
|
|
110
180
|
return;
|
|
111
181
|
}
|
|
112
182
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
req.on('data', chunk => body += chunk);
|
|
116
|
-
req.on('end', async () => {
|
|
117
|
-
try {
|
|
118
|
-
const json = JSON.parse(body);
|
|
119
|
-
await handleRequest(json);
|
|
120
|
-
} catch (e) {
|
|
121
|
-
res.statusCode = 400;
|
|
122
|
-
res.end(JSON.stringify({
|
|
123
|
-
error: {
|
|
124
|
-
code: ErrorCode.INVALID_REQUEST,
|
|
125
|
-
message: 'Invalid JSON'
|
|
126
|
-
}
|
|
127
|
-
}));
|
|
128
|
-
}
|
|
129
|
-
});
|
|
183
|
+
res.statusCode = 404;
|
|
184
|
+
res.end(JSON.stringify({ error: { code: ErrorCode.NOT_FOUND, message: 'Not Found' } }));
|
|
130
185
|
};
|
|
131
186
|
}
|
|
187
|
+
|
|
188
|
+
// Helper to process body
|
|
189
|
+
async function processBody(req: IncomingMessage & { body?: any }, callback: (json: any) => Promise<void>, res: ServerResponse) {
|
|
190
|
+
if (req.body && typeof req.body === 'object') {
|
|
191
|
+
return callback(req.body);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let body = '';
|
|
195
|
+
req.on('data', chunk => body += chunk);
|
|
196
|
+
req.on('end', async () => {
|
|
197
|
+
try {
|
|
198
|
+
const json = body ? JSON.parse(body) : {};
|
|
199
|
+
await callback(json);
|
|
200
|
+
} catch (e) {
|
|
201
|
+
res.statusCode = 400;
|
|
202
|
+
res.end(JSON.stringify({
|
|
203
|
+
error: {
|
|
204
|
+
code: 'INVALID_JSON',
|
|
205
|
+
message: 'Invalid JSON body'
|
|
206
|
+
}
|
|
207
|
+
}));
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|