@genesislcap/mock-server 15.51.1 → 15.53.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/README.md +408 -39
- package/dist/db/criteria.d.ts +13 -0
- package/dist/db/criteria.js +60 -21
- package/dist/db/identity.d.ts +11 -0
- package/dist/db/identity.js +46 -0
- package/dist/db/ordering.d.ts +8 -0
- package/dist/db/ordering.js +49 -0
- package/dist/db/schema.d.ts +62 -0
- package/dist/db/schema.js +490 -0
- package/dist/db/store.d.ts +20 -3
- package/dist/db/store.js +300 -50
- package/dist/handlers/commitEvent.js +47 -9
- package/dist/handlers/crudEvents.d.ts +2 -0
- package/dist/handlers/crudEvents.js +138 -0
- package/dist/handlers/dataLogon.js +105 -6
- package/dist/handlers/eventValidation.d.ts +5 -0
- package/dist/handlers/eventValidation.js +29 -0
- package/dist/handlers/jsonSchema.d.ts +4 -2
- package/dist/handlers/jsonSchema.js +222 -61
- package/dist/handlers/meta.d.ts +6 -1
- package/dist/handlers/meta.js +168 -21
- package/dist/handlers/requestReply.js +519 -30
- package/dist/handlers/resourceAuth.d.ts +1 -0
- package/dist/handlers/resourceAuth.js +14 -0
- package/dist/handlers/resources.js +3 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/protocol/connection.d.ts +2 -0
- package/dist/protocol/errors.d.ts +100 -2
- package/dist/protocol/errors.js +300 -12
- package/dist/protocol/fieldTypes.d.ts +10 -0
- package/dist/protocol/fieldTypes.js +107 -0
- package/dist/protocol/gsfSchemas.d.ts +9 -0
- package/dist/protocol/gsfSchemas.js +423 -0
- package/dist/protocol/messageTypes.d.ts +1 -0
- package/dist/protocol/messageTypes.js +1 -0
- package/dist/protocol/msgNack.d.ts +2 -0
- package/dist/protocol/msgNack.js +10 -0
- package/dist/protocol/rowUpdate.d.ts +5 -3
- package/dist/protocol/rowUpdate.js +33 -15
- package/dist/protocol/schemaValidation.d.ts +11 -0
- package/dist/protocol/schemaValidation.js +186 -0
- package/dist/server.js +95 -29
- package/dist/types.d.ts +45 -0
- package/package.json +1 -1
- package/src/db/criteria.ts +72 -21
- package/src/db/identity.ts +53 -0
- package/src/db/ordering.ts +56 -0
- package/src/db/schema.ts +672 -0
- package/src/db/store.ts +343 -50
- package/src/handlers/commitEvent.ts +54 -10
- package/src/handlers/crudEvents.ts +181 -0
- package/src/handlers/dataLogon.ts +127 -7
- package/src/handlers/eventValidation.ts +43 -0
- package/src/handlers/jsonSchema.ts +302 -69
- package/src/handlers/meta.ts +227 -23
- package/src/handlers/requestReply.ts +626 -33
- package/src/handlers/resourceAuth.ts +18 -0
- package/src/handlers/resources.ts +3 -0
- package/src/index.ts +17 -1
- package/src/protocol/connection.ts +10 -2
- package/src/protocol/errors.ts +361 -13
- package/src/protocol/fieldTypes.ts +115 -0
- package/src/protocol/gsfSchemas.ts +505 -0
- package/src/protocol/messageTypes.ts +1 -0
- package/src/protocol/msgNack.ts +12 -0
- package/src/protocol/rowUpdate.ts +40 -20
- package/src/protocol/schemaValidation.ts +208 -0
- package/src/server.ts +106 -28
- package/src/types.ts +187 -5
package/dist/handlers/meta.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import { type ResolvedField } from '../db/schema.ts';
|
|
1
2
|
import type { Store } from '../db/store.ts';
|
|
2
3
|
import type { Connection } from '../protocol/connection.ts';
|
|
3
|
-
import type { GenesisMessage, MockServerConfig } from '../types.ts';
|
|
4
|
+
import type { GenesisMessage, MockServerConfig, Row } from '../types.ts';
|
|
5
|
+
export declare function eventMetaField(field: ResolvedField, { annotated, appName }: {
|
|
6
|
+
annotated: boolean;
|
|
7
|
+
appName?: string;
|
|
8
|
+
}): Row;
|
|
4
9
|
export declare function handleMetaRequest(message: GenesisMessage, connection: Connection, config: MockServerConfig, store: Store): void;
|
package/dist/handlers/meta.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { eventDefaultOf, eventFields, knownValues, querySchema, requestFieldsOf, requestReplyReplySchema, requestReplySchema, requestReplySourceSchema, resolveEventTarget, } from "../db/schema.js";
|
|
2
2
|
import { isLegacyFidelity } from "../protocol/fidelity.js";
|
|
3
|
+
import { enumClassOf, eventMetaTypeOf, jsonTypeOf, jvmClassOf, rangeOf, titleCase, } from "../protocol/fieldTypes.js";
|
|
4
|
+
import { eventReplyMeta } from "../protocol/gsfSchemas.js";
|
|
3
5
|
import { ERROR_CODE, MESSAGE_TYPE } from "../protocol/messageTypes.js";
|
|
4
6
|
import { resourceNotMappedNack } from "../protocol/msgNack.js";
|
|
5
7
|
import { hiddenFields, requestingUser } from "./resourceAuth.js";
|
|
@@ -9,19 +11,145 @@ import { hiddenFields, requestingUser } from "./resourceAuth.js";
|
|
|
9
11
|
function resolveResourceName(message) {
|
|
10
12
|
return (message.DETAILS?.FEATURE ?? message.DETAILS?.DATASOURCE_NAME ?? message.DETAILS?.RESOURCE_NAME);
|
|
11
13
|
}
|
|
14
|
+
// A FIELD / REQUEST_FIELD / REPLY_FIELD entry: VALID_VALUES on ENUM fields
|
|
15
|
+
// only (DataServerMetaData.kt, ReqRepMetaData.kt).
|
|
16
|
+
function fieldMetadata(field) {
|
|
17
|
+
return {
|
|
18
|
+
NAME: field.name,
|
|
19
|
+
TYPE: field.type,
|
|
20
|
+
...(field.type === 'ENUM' && knownValues(field) ? { VALID_VALUES: knownValues(field) } : {}),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// Index-less queries report the unnamed RECORD_ID index the real dataserver
|
|
24
|
+
// adds (QueryDefinition.DEFAULT_RECORD_ID_INDEX).
|
|
25
|
+
const DEFAULT_INDEXES = [{ NAME: null, FIELDS: 'RECORD_ID' }];
|
|
26
|
+
function dataserverMeta(name, query, config, store, hidden) {
|
|
27
|
+
const fields = querySchema(query, config, store).fields.filter((field) => !hidden.has(field.name));
|
|
28
|
+
return {
|
|
29
|
+
TYPE: 'DATASERVER',
|
|
30
|
+
NAME: name,
|
|
31
|
+
FIELD: fields.map(fieldMetadata),
|
|
32
|
+
INDEXES: query.indexes?.length
|
|
33
|
+
? query.indexes.map((index) => ({ NAME: index.name, FIELDS: index.fields.join(' ') }))
|
|
34
|
+
: DEFAULT_INDEXES,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// The two request-server shapes of ReqRepMetaData.kt: criteria-only
|
|
38
|
+
// (REQUEST_FIELD: [], SORTABLE_FIELDS, CRITERIA_FIELDS, no INDICES) and plain
|
|
39
|
+
// (REQUEST_FIELD from the request block, INDICES: [],
|
|
40
|
+
// CRITERIA_ONLY_REQUEST: false). REPLY_FIELD is the reply block, else the
|
|
41
|
+
// source's fields then RECORD_ID and TIMESTAMP, for either
|
|
42
|
+
// (requestReplyReplySchema). SORTABLE_FIELDS and CRITERIA_FIELDS are the
|
|
43
|
+
// entity's fields whatever the reply block says (the dictionary entry's
|
|
44
|
+
// nativeQueryFields, RequestReplyServer.kt).
|
|
45
|
+
function requestServerMeta(name, requestReply, config, store, hidden) {
|
|
46
|
+
const visible = (field) => !hidden.has(field.name);
|
|
47
|
+
const replyFields = requestReplyReplySchema(requestReply, config, store).fields.filter(visible);
|
|
48
|
+
const entityFields = requestReplySourceSchema(requestReply, config, store).fields;
|
|
49
|
+
if (requestReply.criteriaOnly) {
|
|
50
|
+
const names = entityFields.filter(visible).map((field) => field.name);
|
|
51
|
+
return {
|
|
52
|
+
TYPE: 'REQUEST_SERVER',
|
|
53
|
+
REQUEST_FIELD: [],
|
|
54
|
+
CRITERIA_ONLY_REQUEST: true,
|
|
55
|
+
SORTABLE_FIELDS: names,
|
|
56
|
+
CRITERIA_FIELDS: names,
|
|
57
|
+
REPLY_FIELD: replyFields.map(fieldMetadata),
|
|
58
|
+
NAME: name,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const requestFields = requestFieldsOf(requestReply, store).map((fieldName) => entityFields.find((field) => field.name === fieldName) ?? {
|
|
62
|
+
name: fieldName,
|
|
63
|
+
type: 'STRING',
|
|
64
|
+
nullable: false,
|
|
65
|
+
generated: false,
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
TYPE: 'REQUEST_SERVER',
|
|
69
|
+
REQUEST_FIELD: requestFields.map(fieldMetadata),
|
|
70
|
+
INDICES: [],
|
|
71
|
+
CRITERIA_ONLY_REQUEST: false,
|
|
72
|
+
REPLY_FIELD: replyFields.map(fieldMetadata),
|
|
73
|
+
NAME: name,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
// One FIELD entry of an event's META (EventMeta's MetaField, flattened as the
|
|
77
|
+
// real server serializes it). Only a table event's class (a generated DAO)
|
|
78
|
+
// carries the dictionary's annotations: TITLE, MIN_LENGTH / MAX_LENGTH,
|
|
79
|
+
// MIN / MAX and GENESIS_TYPE. A delete's <Table>.ById and a custom DTO have
|
|
80
|
+
// none of them (GENESIS_TYPE null). An ENUM always names its values, its
|
|
81
|
+
// class and a DEFAULT_VALUE (the class's first value when the field has no
|
|
82
|
+
// default); DEFAULT appears only when the field has a default.
|
|
83
|
+
export function eventMetaField(field, { annotated, appName }) {
|
|
84
|
+
const type = field.type;
|
|
85
|
+
const enumClass = () => enumClassOf(field.table, field.name, appName);
|
|
86
|
+
const values = knownValues(field);
|
|
87
|
+
const fieldDefault = eventDefaultOf(field);
|
|
88
|
+
const meta = { NAME: field.name };
|
|
89
|
+
if (annotated)
|
|
90
|
+
meta.TITLE = field.title ?? titleCase(field.name);
|
|
91
|
+
meta.DESCRIPTION = field.description ?? jvmClassOf(type, enumClass);
|
|
92
|
+
meta.NULLABLE = field.nullable;
|
|
93
|
+
if (annotated && type === 'STRING') {
|
|
94
|
+
if (field.minLength !== undefined)
|
|
95
|
+
meta.MIN_LENGTH = field.minLength;
|
|
96
|
+
if (field.maxSize !== undefined)
|
|
97
|
+
meta.MAX_LENGTH = field.maxSize;
|
|
98
|
+
}
|
|
99
|
+
const range = annotated ? rangeOf(type) : undefined;
|
|
100
|
+
if (range)
|
|
101
|
+
[meta.MIN, meta.MAX] = range;
|
|
102
|
+
if (type === 'ENUM') {
|
|
103
|
+
// The enum class's own default: the declared one, else its first value.
|
|
104
|
+
if (values)
|
|
105
|
+
meta.VALID_VALUES = values;
|
|
106
|
+
const typeDefault = field.default ?? values?.[0];
|
|
107
|
+
if (typeDefault !== undefined)
|
|
108
|
+
meta.DEFAULT_VALUE = typeDefault;
|
|
109
|
+
meta.ENUM_CLASS = enumClass();
|
|
110
|
+
}
|
|
111
|
+
meta.TYPE = eventMetaTypeOf(type);
|
|
112
|
+
meta.JSON_TYPE = jsonTypeOf(type);
|
|
113
|
+
if (type !== 'ENUM' && field.default !== undefined)
|
|
114
|
+
meta.DEFAULT_VALUE = field.default;
|
|
115
|
+
// OPTIONAL is about the payload, resolved by eventFields (schema.ts).
|
|
116
|
+
meta.OPTIONAL = field.optional ?? false;
|
|
117
|
+
meta.READ_ONLY = false;
|
|
118
|
+
if (fieldDefault !== undefined)
|
|
119
|
+
meta.DEFAULT = fieldDefault;
|
|
120
|
+
meta.GENESIS_TYPE = annotated ? type : null;
|
|
121
|
+
return meta;
|
|
122
|
+
}
|
|
123
|
+
function eventMeta(name, target, fields, appName) {
|
|
124
|
+
const annotated = target.kind === 'table';
|
|
125
|
+
return {
|
|
126
|
+
NAME: name,
|
|
127
|
+
...(target.title ? { TITLE: target.title } : {}),
|
|
128
|
+
DESCRIPTION: target.className,
|
|
129
|
+
FIELD: fields.map((field) => eventMetaField(field, { annotated, appName })),
|
|
130
|
+
DEFINITIONS: {},
|
|
131
|
+
REPLY: eventReplyMeta(),
|
|
132
|
+
REQUIRES_APPROVAL: false,
|
|
133
|
+
SCHEDULABLE: false,
|
|
134
|
+
TYPE: 'EVENT_HANDLER',
|
|
135
|
+
};
|
|
136
|
+
}
|
|
12
137
|
// Connect.getMetadata reads metaResponse.DETAILS (not the top-level message)
|
|
13
138
|
// and extractFieldDefinitions (metadata.utils.ts) branches on DETAILS.TYPE:
|
|
14
139
|
// DATASERVER resources need DETAILS.FIELD, REQUEST_SERVER ones need
|
|
15
140
|
// DETAILS.REPLY_FIELD. Get either wrong and every grid silently reports
|
|
16
|
-
// "Metadata Fetch failed" / "Unsupported resource type: undefined".
|
|
17
|
-
//
|
|
18
|
-
//
|
|
141
|
+
// "Metadata Fetch failed" / "Unsupported resource type: undefined". Event
|
|
142
|
+
// META is what the AI assistant builds its write tools from
|
|
143
|
+
// (ai-assistant/src/genesis/event-fields.ts): no event META, no write tools.
|
|
144
|
+
// Request servers answer by resource name (TRADE); REQ_TRADE is not a
|
|
145
|
+
// resource name and gets the router's 404, as on the real server.
|
|
19
146
|
export function handleMetaRequest(message, connection, config, store) {
|
|
20
147
|
const resourceName = resolveResourceName(message);
|
|
21
148
|
const query = config.queries?.[resourceName];
|
|
22
149
|
const requestReply = config.requestReplies?.[resourceName];
|
|
23
150
|
const legacy = isLegacyFidelity(config);
|
|
24
|
-
|
|
151
|
+
const event = query || requestReply || legacy ? undefined : resolveEventTarget(resourceName, config);
|
|
152
|
+
if (!query && !requestReply && !event) {
|
|
25
153
|
connection.reply(message, legacy
|
|
26
154
|
? {
|
|
27
155
|
MESSAGE_TYPE: message.MESSAGE_TYPE,
|
|
@@ -32,20 +160,39 @@ export function handleMetaRequest(message, connection, config, store) {
|
|
|
32
160
|
: resourceNotMappedNack(resourceName));
|
|
33
161
|
return;
|
|
34
162
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
163
|
+
if (legacy) {
|
|
164
|
+
connection.reply(message, {
|
|
165
|
+
MESSAGE_TYPE: message.MESSAGE_TYPE,
|
|
166
|
+
DETAILS: legacyMeta(resourceName, query, requestReply, connection, config, store),
|
|
167
|
+
});
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
let details;
|
|
171
|
+
if (event) {
|
|
172
|
+
details = eventMeta(resourceName, event, eventFields(event, config, store), config.appName);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
// Hidden fields disappear from the metadata too — a column the user can't
|
|
176
|
+
// see must not be offered to the grid at all.
|
|
177
|
+
const hidden = hiddenFields((query ?? requestReply).auth, requestingUser(connection));
|
|
178
|
+
details = query
|
|
179
|
+
? dataserverMeta(resourceName, query, config, store, hidden)
|
|
180
|
+
: requestServerMeta(resourceName, requestReply, config, store, hidden);
|
|
181
|
+
}
|
|
182
|
+
connection.reply(message, { MESSAGE_TYPE: MESSAGE_TYPE.FEATURE_ACK, DETAILS: details });
|
|
183
|
+
}
|
|
184
|
+
// What this engine answered up to 15.47: {NAME, TYPE} fields only, KEY: []
|
|
185
|
+
// on dataservers, REPLY_FIELD alone on request servers, and no event META.
|
|
186
|
+
// Declared types (and query fields blocks) still apply.
|
|
187
|
+
function legacyMeta(resourceName, query, requestReply, connection, config, store) {
|
|
188
|
+
const hidden = hiddenFields((query ?? requestReply).auth, requestingUser(connection));
|
|
189
|
+
const schema = query
|
|
190
|
+
? querySchema(query, config, store)
|
|
191
|
+
: requestReplySchema(requestReply, config, store);
|
|
192
|
+
const fields = schema.fields
|
|
193
|
+
.filter((field) => !hidden.has(field.name))
|
|
194
|
+
.map((field) => ({ NAME: field.name, TYPE: field.type }));
|
|
195
|
+
return query
|
|
196
|
+
? { NAME: resourceName, TYPE: 'DATASERVER', FIELD: fields, KEY: [] }
|
|
197
|
+
: { NAME: resourceName, TYPE: 'REQUEST_SERVER', REPLY_FIELD: fields };
|
|
51
198
|
}
|