@ti-engine/web-framework 1.25.1 → 1.26.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
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
This document will contain the list of changes made to the framework. The format is based on the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification.
|
|
4
4
|
|
|
5
|
+
## Version 1.26.0
|
|
6
|
+
|
|
7
|
+
* feat(config-management): report stored documents that no longer satisfy their registered schema.
|
|
8
|
+
`ConfigService.listSchemaViolations()` returns one entry per registered document whose **stored** value fails its
|
|
9
|
+
schema, and `ConfigRegistry.validateSchema( configKey, value )` exposes the schema half of validation on its own.
|
|
10
|
+
Nothing validated a stored value on the way out before this: the store hands back whatever it holds and a consumer
|
|
11
|
+
freezes it into its config exports, so a release that tightens a schema — adding a required top-level key, say —
|
|
12
|
+
leaves any deployment seeded before the change serving a document that can no longer be saved. The failure used to
|
|
13
|
+
surface much later, as an admin edit rejected for a key the admin never touched, in a screen unrelated to the
|
|
14
|
+
change. Schema-only by design: a semantic validator may need a `ValidatorContext` the caller has no reason to
|
|
15
|
+
build, and several are about an *edit* rather than the document standing alone. Read-only by design too —
|
|
16
|
+
reconciling a stale document is the drift panel's job, under audit; an automatic repair here would write config
|
|
17
|
+
outside the change-set machinery. A document that has never been stored is not a violation.
|
|
18
|
+
* refactor(config-management): `ConfigRegistry.validate()` now delegates its schema step to `validateSchema()`, so
|
|
19
|
+
the two can never disagree about what is structurally admissible.
|
|
20
|
+
|
|
5
21
|
## Version 1.25.1
|
|
6
22
|
|
|
7
23
|
* fix(ti-framework): resolve a label key whose own name contains a literal dot. `getLabel` split the key on every
|
|
@@ -197,13 +197,7 @@ class ConfigRegistry {
|
|
|
197
197
|
return Promise.reject( exceptions.raise( exceptions.exceptionCode.E_WEB_INVALID_REQUEST_PARAMETERS, { reason: "unknown-config", configKey: configKey } ) );
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
const errors =
|
|
201
|
-
const validateSchema = this.#schemaValidator( registration );
|
|
202
|
-
if ( !validateSchema( value ) ) {
|
|
203
|
-
for ( const error of ( validateSchema.errors || [] ) ) {
|
|
204
|
-
errors.push( { path: error.dataPath || instancePathToDataPath( error.instancePath ), message: error.message || "schema violation", code: "schema", params: error.params } );
|
|
205
|
-
}
|
|
206
|
-
}
|
|
200
|
+
const errors = this.validateSchema( configKey, value ).errors.slice();
|
|
207
201
|
|
|
208
202
|
return Promise.all( registration.validators.map( ( validator ) => {
|
|
209
203
|
return Promise.resolve( validator( value, context ) ).then( ( issues ) => ( Array.isArray( issues ) ? issues : [] ) );
|
|
@@ -217,6 +211,38 @@ class ConfigRegistry {
|
|
|
217
211
|
} );
|
|
218
212
|
}
|
|
219
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Validates a value against a document's registered schema **only**, skipping the semantic validators.
|
|
216
|
+
* <br/>
|
|
217
|
+
* Separate from {@link ConfigRegistry#validate} because the two answer different questions. A semantic validator
|
|
218
|
+
* may need a {@link ValidatorContext} the caller has no reason to build, and several of them are about an edit
|
|
219
|
+
* (did the version move? does this removal orphan a reference?) rather than about the document standing alone.
|
|
220
|
+
* A caller that just wants to know whether a value is structurally admissible — a boot-time check on what the
|
|
221
|
+
* store already holds, say — needs this half and not the other.
|
|
222
|
+
*
|
|
223
|
+
* @method
|
|
224
|
+
* @param {string} configKey
|
|
225
|
+
* @param {Object} value
|
|
226
|
+
* @returns {{valid: boolean, errors: ConfigValidationIssue[]}}
|
|
227
|
+
* @throws {TiException.E_WEB_INVALID_REQUEST_PARAMETERS} If the document is not registered.
|
|
228
|
+
* @public
|
|
229
|
+
*/
|
|
230
|
+
validateSchema( configKey, value ) {
|
|
231
|
+
const registration = this.#registrations.get( configKey );
|
|
232
|
+
if ( !registration ) {
|
|
233
|
+
throw exceptions.raise( exceptions.exceptionCode.E_WEB_INVALID_REQUEST_PARAMETERS, { reason: "unknown-config", configKey: configKey } );
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const errors = [];
|
|
237
|
+
const validateSchema = this.#schemaValidator( registration );
|
|
238
|
+
if ( !validateSchema( value ) ) {
|
|
239
|
+
for ( const error of ( validateSchema.errors || [] ) ) {
|
|
240
|
+
errors.push( { path: error.dataPath || instancePathToDataPath( error.instancePath ), message: error.message || "schema violation", code: "schema", params: error.params } );
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return { valid: errors.length === 0, errors: errors };
|
|
244
|
+
}
|
|
245
|
+
|
|
220
246
|
/* Private interface */
|
|
221
247
|
|
|
222
248
|
/**
|
|
@@ -21,6 +21,7 @@ const configDrift = require( "#config-drift" );
|
|
|
21
21
|
/** @import ConfigChangeNotifier from "#config-change-notifier" */
|
|
22
22
|
/** @import ConfigRegistry from "#config-registry" */
|
|
23
23
|
/** @import ConfigStore from "#config-store" */
|
|
24
|
+
/** @import { ConfigValidationIssue } from "#config-registry" */
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Orchestrates validated, versioned configuration edits on top of {@link ConfigStore} and {@link ConfigRegistry}.
|
|
@@ -374,6 +375,37 @@ class ConfigService {
|
|
|
374
375
|
} ) );
|
|
375
376
|
}
|
|
376
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Reports every registered document whose **stored** value no longer satisfies its registered schema.
|
|
380
|
+
* <br/>
|
|
381
|
+
* Nothing validates a stored value on the way out: the store hands back whatever it holds, and a consumer
|
|
382
|
+
* freezes it into its config exports. That is fine until a release tightens a schema — adds a required
|
|
383
|
+
* top-level key, say — at which point a deployment seeded before the change keeps serving a document that
|
|
384
|
+
* cannot be saved any more. The failure surfaces much later, as an admin edit rejected for a key the admin
|
|
385
|
+
* never touched, in a screen that has nothing to do with the change.
|
|
386
|
+
* <br/>
|
|
387
|
+
* This answers that question at a moment when it is cheap to act on. It is deliberately schema-only (see
|
|
388
|
+
* {@link ConfigRegistry#validateSchema}) and deliberately read-only: reconciling a stale document is what the
|
|
389
|
+
* drift panel is for, and an automatic repair here would write config outside the audited change-set machinery.
|
|
390
|
+
* A document that has never been stored is not a violation and is omitted.
|
|
391
|
+
*
|
|
392
|
+
* @method
|
|
393
|
+
* @returns {Promise<Array<{configKey: string, errors: ConfigValidationIssue[]}>>} Empty when every stored
|
|
394
|
+
* document validates.
|
|
395
|
+
* @public
|
|
396
|
+
*/
|
|
397
|
+
listSchemaViolations() {
|
|
398
|
+
return Promise.all( this.#registry.list().map( ( configKey ) => {
|
|
399
|
+
return this.#store.getCurrent( configKey ).then( ( current ) => {
|
|
400
|
+
if ( !current ) {
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
const result = this.#registry.validateSchema( configKey, current.value );
|
|
404
|
+
return result.valid ? null : { configKey: configKey, errors: result.errors };
|
|
405
|
+
} );
|
|
406
|
+
} ) ).then( ( results ) => results.filter( Boolean ) );
|
|
407
|
+
}
|
|
408
|
+
|
|
377
409
|
/**
|
|
378
410
|
* Applies the registered file defaults for the given documents, as a single validated change-set.
|
|
379
411
|
* <br/>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ti-engine/web-framework",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.26.0",
|
|
4
4
|
"description": "A web-framework based on the ti-engine. It provides a customizable ready-to-use web-server microservice and a set of tools for creating web applications. NOTICE: This is still a work in progress and the full architecture, design, and functionality are not available!",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ti-engine",
|
|
@@ -142,6 +142,26 @@ declare class ConfigRegistry {
|
|
|
142
142
|
valid: boolean;
|
|
143
143
|
errors: ConfigValidationIssue[];
|
|
144
144
|
}>;
|
|
145
|
+
/**
|
|
146
|
+
* Validates a value against a document's registered schema **only**, skipping the semantic validators.
|
|
147
|
+
* <br/>
|
|
148
|
+
* Separate from {@link ConfigRegistry#validate} because the two answer different questions. A semantic validator
|
|
149
|
+
* may need a {@link ValidatorContext} the caller has no reason to build, and several of them are about an edit
|
|
150
|
+
* (did the version move? does this removal orphan a reference?) rather than about the document standing alone.
|
|
151
|
+
* A caller that just wants to know whether a value is structurally admissible — a boot-time check on what the
|
|
152
|
+
* store already holds, say — needs this half and not the other.
|
|
153
|
+
*
|
|
154
|
+
* @method
|
|
155
|
+
* @param {string} configKey
|
|
156
|
+
* @param {Object} value
|
|
157
|
+
* @returns {{valid: boolean, errors: ConfigValidationIssue[]}}
|
|
158
|
+
* @throws {TiException.E_WEB_INVALID_REQUEST_PARAMETERS} If the document is not registered.
|
|
159
|
+
* @public
|
|
160
|
+
*/
|
|
161
|
+
validateSchema(configKey: string, value: Object): {
|
|
162
|
+
valid: boolean;
|
|
163
|
+
errors: ConfigValidationIssue[];
|
|
164
|
+
};
|
|
145
165
|
}
|
|
146
166
|
declare namespace ConfigRegistry {
|
|
147
167
|
export { instance };
|
|
@@ -2,9 +2,11 @@ export = ConfigService;
|
|
|
2
2
|
import type ConfigChangeNotifier from "#config-change-notifier";
|
|
3
3
|
import type ConfigRegistry from "#config-registry";
|
|
4
4
|
import type ConfigStore from "#config-store";
|
|
5
|
+
import type { ConfigValidationIssue } from "#config-registry";
|
|
5
6
|
/** @import ConfigChangeNotifier from "#config-change-notifier" */
|
|
6
7
|
/** @import ConfigRegistry from "#config-registry" */
|
|
7
8
|
/** @import ConfigStore from "#config-store" */
|
|
9
|
+
/** @import { ConfigValidationIssue } from "#config-registry" */
|
|
8
10
|
/**
|
|
9
11
|
* Orchestrates validated, versioned configuration edits on top of {@link ConfigStore} and {@link ConfigRegistry}.
|
|
10
12
|
*
|
|
@@ -223,6 +225,29 @@ declare class ConfigService {
|
|
|
223
225
|
* @public
|
|
224
226
|
*/
|
|
225
227
|
listDrift(): Promise<Array<Object>>;
|
|
228
|
+
/**
|
|
229
|
+
* Reports every registered document whose **stored** value no longer satisfies its registered schema.
|
|
230
|
+
* <br/>
|
|
231
|
+
* Nothing validates a stored value on the way out: the store hands back whatever it holds, and a consumer
|
|
232
|
+
* freezes it into its config exports. That is fine until a release tightens a schema — adds a required
|
|
233
|
+
* top-level key, say — at which point a deployment seeded before the change keeps serving a document that
|
|
234
|
+
* cannot be saved any more. The failure surfaces much later, as an admin edit rejected for a key the admin
|
|
235
|
+
* never touched, in a screen that has nothing to do with the change.
|
|
236
|
+
* <br/>
|
|
237
|
+
* This answers that question at a moment when it is cheap to act on. It is deliberately schema-only (see
|
|
238
|
+
* {@link ConfigRegistry#validateSchema}) and deliberately read-only: reconciling a stale document is what the
|
|
239
|
+
* drift panel is for, and an automatic repair here would write config outside the audited change-set machinery.
|
|
240
|
+
* A document that has never been stored is not a violation and is omitted.
|
|
241
|
+
*
|
|
242
|
+
* @method
|
|
243
|
+
* @returns {Promise<Array<{configKey: string, errors: ConfigValidationIssue[]}>>} Empty when every stored
|
|
244
|
+
* document validates.
|
|
245
|
+
* @public
|
|
246
|
+
*/
|
|
247
|
+
listSchemaViolations(): Promise<Array<{
|
|
248
|
+
configKey: string;
|
|
249
|
+
errors: ConfigValidationIssue[];
|
|
250
|
+
}>>;
|
|
226
251
|
/**
|
|
227
252
|
* Applies the registered file defaults for the given documents, as a single validated change-set.
|
|
228
253
|
* <br/>
|