feat(cuod-bundle): JSON Schema драфты для spacecraft/satellite_type/ground_station + auto-import
3 schema драфта в classpath:/bundles/cuod/: - spacecraft.schema.json: КА с COSPAR designator, NORAD ID, multi-locale name/operator/mission, orbit params (type/semimajor/eccentricity/inclination/ raan/aop/epoch), spectrum_bands (12 bands), launch/decay dates, status enum - satellite_type.schema.json: классификация КА — code, multi-locale name/desc, mission_category enum (10 categories), mass_class, typical_orbit_classes, typical_resolution_m, typical_revisit_hours - ground_station.schema.json: code, multi-locale name/location/operator, country (ISO 3166), lat/lon/elevation, antennas array (id, diameter, bands, polarizations, tx/rx_capable, G/T, EIRP), status manifest.json: bundle 'cuod' v1.0.0, 3 dictionaries с supported_locales [ru-RU, en-US] default ru-RU, scope PUBLIC ordinis-cuod-bundle модуль: - BundleManifest record + DictionaryEntry record (Jackson deserializer) - CuodBundleProperties @ConfigurationProperties (auto-import flag, update-existing-schema flag, resource-root path) - CuodBundleImporter @Service (proxified, @Transactional работает) — читает manifest + schemas, для каждого: findByName → если нет, создать definition + outbox event DefinitionCreated; если есть, проверить schema_version, при отличии и update_existing_schema=true — обновить + DefinitionUpdated outbox - CuodBundleStartupRunner @Configuration с ApplicationRunner — триггерит importBundle при старте Spring Boot. Отделено от Importer чтобы Importer оставался proxified (@Transactional на importBundle vs lambda не работает) ordinis-validation FIX: - RecordValidator теперь учитывает required array родительской схемы. Отсутствие x-localized optional поля (например mission, operator) больше не считается ошибкой. Required x-localized поля по-прежнему обязательны. E2E проверено: - ORDINIS_BUNDLE_CUOD_AUTO_IMPORT=true → 3 справочника создались с правильными scope, locales, schema_version - POST spacecraft с реальными Kanopus-V данными (orbit + spectrum_bands) → 201 - POST satellite_type с code='lower-case-bad' → 422 pattern error - Idempotent: повторный старт пропускает existing без ошибок
This commit is contained in:
+15
-4
@@ -63,7 +63,8 @@ public class RecordValidator {
|
||||
|
||||
/**
|
||||
* Рекурсивный обход schema. Для каждого {@code properties[name]} с
|
||||
* {@code x-localized: true} — проверяет значение в data.
|
||||
* {@code x-localized: true} — проверяет значение в data, учитывая required
|
||||
* массив родительской схемы (optional поля не требуют значения).
|
||||
*/
|
||||
private void walkLocalizedFields(
|
||||
JsonNode schema,
|
||||
@@ -78,6 +79,12 @@ public class RecordValidator {
|
||||
JsonNode properties = schema.get("properties");
|
||||
if (properties == null || !properties.isObject()) return;
|
||||
|
||||
Set<String> requiredFields = new HashSet<>();
|
||||
JsonNode required = schema.get("required");
|
||||
if (required != null && required.isArray()) {
|
||||
required.forEach(n -> { if (n.isTextual()) requiredFields.add(n.asText()); });
|
||||
}
|
||||
|
||||
Iterator<Map.Entry<String, JsonNode>> it = properties.fields();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<String, JsonNode> entry = it.next();
|
||||
@@ -86,10 +93,11 @@ public class RecordValidator {
|
||||
String fieldPath = path.isEmpty() ? "/" + fieldName : path + "/" + fieldName;
|
||||
|
||||
JsonNode dataField = data == null ? null : data.get(fieldName);
|
||||
boolean fieldIsRequired = requiredFields.contains(fieldName);
|
||||
|
||||
JsonNode xLocalized = fieldSchema.get("x-localized");
|
||||
if (xLocalized != null && xLocalized.isBoolean() && xLocalized.asBoolean()) {
|
||||
validateLocalizedValue(fieldPath, dataField, supported, defaultLocale, errors);
|
||||
validateLocalizedValue(fieldPath, dataField, fieldIsRequired, supported, defaultLocale, errors);
|
||||
} else if (fieldSchema.has("properties")) {
|
||||
walkLocalizedFields(fieldSchema, dataField, fieldPath, supported, defaultLocale, errors);
|
||||
}
|
||||
@@ -99,13 +107,16 @@ public class RecordValidator {
|
||||
private void validateLocalizedValue(
|
||||
String path,
|
||||
JsonNode value,
|
||||
boolean isRequired,
|
||||
Set<String> supported,
|
||||
String defaultLocale,
|
||||
List<ValidationError> errors) {
|
||||
|
||||
if (value == null || value.isNull()) {
|
||||
errors.add(new ValidationError(path, "x-localized.missing",
|
||||
"Localized field is required (must be object with locale keys)"));
|
||||
if (isRequired) {
|
||||
errors.add(new ValidationError(path, "x-localized.missing",
|
||||
"Localized field is required (must be object with locale keys)"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!value.isObject()) {
|
||||
|
||||
Reference in New Issue
Block a user