Commit Graph

17 Commits

Author SHA1 Message Date
Zimin A.N. 656da8c9b9 feat(cuod): spacecraft.mission — x-localized текст → FK на mission.code
mission у КА хранился свободным x-localized текстом (краткое описание),
дублировался, рассинхронизировался, не был связан со справочником
mission (где уже есть программы миссий + поле spacecraft_codes).

Теперь mission — строка-код, x-references → mission.code. Референсная
целостность + единый источник.

Маппинг 14 КА → 9 миссий взят из mission.spacecraft_codes (обратная
связь уже была в данных). Проверено: все 14 мапятся однозначно,
0 пропусков, 0 конфликтов.

- 0027-spacecraft-mission-fk.xml — Liquibase-миграция существующих БД:
  схема + 14 записей одной транзакцией прямым SQL (смена типа поля —
  chicken-and-egg, как 0020 raw SQL минует Envers/outbox; forward-only).
  schema_version → 2.0.0.
- cuod-bundle: spacecraft.schema.json mission → x-references,
  records/spacecraft.records.json — 14 mission → код, manifest 2.0.0.
  Свежие установки получают новую форму сразу.

Caveat: миграция через raw SQL не пишет Envers _AUD и Kafka outbox —
проекции read-api нужно пересобрать после деплоя (как при 0020).
2026-05-21 16:21:46 +03:00
Zimin A.N. 8adfd39bff feat(bundle): bundle hygiene v2 — manifest spec + signing verifier + docs
Ships marketplace-design-v2 scoped to bundle hygiene (NO consumer UI, NO
install dialog, NO marketplace). 2-3 day cap respected — Approach C narrow
slice. Anti-feature commitment: scope creep → new design doc + /office-hours.

## What ships

### New module ordinis-bundle-spec
- BundleManifest.java — canonical apiVersion: ordinis.io/v1 record
  (Metadata + DictionaryEntry, Jackson-bindable, forward-compat ignoreUnknown)
- ManifestParser.java — YAML → record + structural validate
  (apiVersion/kind enforce, semver regex, bundle-id regex, path traversal
  guard на schemaRef, unique dict names, scope enum)
- BundleSignatureVerifier.java — ed25519 sign/verify (JDK 21 native)
- Zero Spring/JPA/Kafka deps — pure Java + Jackson YAML

### CUOD reference implementation
- ordinis-cuod-bundle/manifest.yaml — 40 dicts canonical entries
- Test fixture pin: cuod-manifest-fixture.yaml parses+validates clean
  (regression guard на каждом CI run)

### Tests
- 12 cases ManifestParser (happy + reject paths)
- 7 cases BundleSignatureVerifier (sign/verify roundtrip, tamper, wrong key)
- 1 case CUOD fixture pin
- Total 20 tests, all green

### Docs
- docs/integration/bundle-format-spec.md — formal spec для Java handoff
- docs/user-guide/bundle-authoring.md — how-to для новых bundle authors

## Why now (N=1 customer)

Per office-hours v2 decision: marketplace UI build = N customers value scale,
but bundle hygiene + sign format = AT N=1 ROI:
- Java team handoff requires documented spec независимо
- Sales demo «here's how customer #2 would ship custom bundle»
- Optionality для marketplace UI когда customer #2 LOI surfaces

## Explicitly NOT shipped (Phase 2 scope if ever)

- Publisher Maven plugin (separate artifact when actual publish needed)
- Browse catalog UI / install dialog (no customer #2 with use case)
- Dep resolution / topological install
- Multi-tenant Nexus namespacing
- Bundle uninstall flow
2026-05-17 10:39:07 +03:00
Zimin A.N. c8dd5aad4e feat(schema): Phase 1 schema templates + Nexus DevOps runbook
## AI Schema Assist Phase 1 (steps 1-2)

Approach A core (templates only, no LLM) — admin начинает создание нового
справочника с curated skeleton вместо пустого Monaco. Approach C (per-field
LLM suggest) — Phase 2 на этом фундаменте.

### Backend
- `templates/*.template.json` в ordinis-cuod-bundle resources — 6 шаблонов
  (blank/spacecraft/ground-station/antenna/frequency-band/operator)
- `SchemaTemplateRegistry` (rest-api) — classpath scan at startup, strip
  x-template-* metadata, expose canonical JSON Schema
- `SchemaTemplatesController` — GET /api/v1/dictionaries/templates (list)
  + /{id} (detail с schemaJson)
- Cross-bundle support: scanner matches classpath*:templates/*.template.json
  — другие bundles могут shippит свои templates через те же conventions

### Frontend
- types: SchemaTemplateSummary + SchemaTemplateDetail
- queries: schemaTemplatesQuery + lazy detail query
- TemplatePicker.tsx — grid of cards (icon + name + description + tags),
  click → fetch detail → fire onPick(detail)
- Integrated в DictionaryEditorDialog (create mode, properties.length===0
  visible — после первой property picker исчезает чтобы не перезатереть)

## Nexus DevOps handoff

`docs-internal/ops/nexus-alpine-mirror.md` — runbook про APKINDEX vs file
inventory drift на v3.23 mirror. Includes:
- Verification commands
- 3 fix options (invalidate cache / scheduled refresh / self-host)
- Prometheus alert proposal
- Current workaround (node:22-alpine3.20 pin) reference

Active issue paired с CI saga !227/!229/!230 — нужен DevOps action.
2026-05-16 13:20:43 +03:00
Zimin A.N. d229c545f9 feat(approval): bundle-driven approval policy + spacecraft/ground_station soak flag
Approval Workflow v2 Phase 3 — soak prep:

- BundleManifest.DictionaryEntry: добавлены опциональные approvalRequired
  (Boolean) и approvalMinRole (String).
- CuodBundleImporter: применяет policy на fresh + on-update + при стабильной
  схеме (flip без bumping schema_version). Apply ТОЛЬКО когда явно изменился
  → не штормим UPDATE'ами.
- CuodBundleProperties.honorApprovalPolicy (default false): env-gated guard.
  Bundle ship'ит approvalRequired:true для пилотных dicts, prod игнорирует
  пока не явно включит.
- manifest cuod 1.3.0 → 1.3.1: spacecraft + ground_station получили
  approvalRequired:true. Без honor flag — no-op.

Этого commit'а достаточно для соответствующего helm change в
ordinis-infra (bundleHonorApprovalPolicy=true в values-staging) — flip'ает
spacecraft/ground_station на staging при следующем deploy. Prod остаётся
default=false до окончания soak.
2026-05-08 14:59:34 +03:00
Zimin A.N. a7cdd5df5c feat(lineage): Phase 4 — SLO metrics + bundle v1.3.0 pilot (warn mode)
dict-relationships-v2 epic, Phase 4 (final). Backend SLO instrumentation +
schema bundle pilot. Alert rules + runbook идут отдельным коммитом в
ordinis-infra.

Backend metrics (Micrometer / Prometheus):
- nsi_dependents_query_duration_seconds (Timer + p50/p95/p99 percentiles)
  labels: mode={schema|record|*_error}, target_dict
- nsi_cascade_close_total (Counter)
  labels: target_dict, outcome={success|blocked|cascade_required|too_large|timeout|error}
- nsi_cascade_close_duration_seconds (Timer + percentiles)
- nsi_lineage_mv_last_refresh_seconds_ago (Gauge, NaN when mv disabled)
- nsi_lineage_mv_row_count (Gauge)
- nsi_lineage_mv_last_duration_seconds (Gauge)
- nsi_lineage_mv_refresh_total{outcome=ok|error} (Counter)

Cardinality bound: target_dict tag — ≤40 dictionaries; outcome — 6 values.
Total time series ≈ 240 per app instance — приемлемо.

Optional MeterRegistry inject — null в test ctor'ах (back-compat).

Bundle v1.3.0 pilot:
- spacecraft.satellite_type_code → satellite_type.code теперь имеет
  x-references-on-close: warn. Закрытие satellite_type больше не
  блокируется через 1.3.0; spacecraft остаётся orphan; orphan scanner
  поднимет metric. Data steward потом repoints.
- bundle version 1.2.1 → 1.3.0, spacecraft schema 1.0.0 → 1.1.0
  (description обновлён с pilot context).
- WARN-mode выбран как safe pilot — validates новый code path без
  destructive cascade. CASCADE-mode flip может быть отдельным PR
  per-relationship после operator review.

Verify:
- mvn verify -pl '!ordinis-app': SUCCESS (5.7s).
- 106/106 tests still green (existing tests не задеты).
- helm lint в ordinis-infra: clean (alerts render correctly).

Backward-compat: REGRESSION test (ReferenceCloseRegressionTest) covers
v1.2.1-style schemas без x-references-on-close. WARN mode на one field
не меняет contract других — bundle import idempotent.

Roadmap: 4 phases  shipped. Cascade engine + lineage UI + materialized
view + SLO metrics live.
2026-05-08 11:34:29 +03:00
Zimin A.N. c12f515f43 fix(cuod-bundle): почистить FK orphans в seed (antenna, deliverable_type, ground_station)
Orphan FK references в seed данных bundle v1.2.1 — references на codes которых
нет в target словарях. Bundle import не валидирует x-references (privileged
data load), поэтому records ингестились с broken FK. На prod такие orphans
видны через ReferenceValidator при попытке update + через OrphanReferenceScanner.

Найдено через scan по 6 FK relations (antenna, consumer, deliverable_type,
orbital_regime, spacecraft×2):

1. antenna.ground_station (4 orphans):
   - GS_MOSCOW → MOSCOW_OTRADNOE (already существует)
   - GS_KHANTY/KRASNOYARSK/KHABAROVSK → добавили новые ground_station records
     (KHANTY_MANSIYSK_NPC, KRASNOYARSK_NPC, KHABAROVSK_NPC) — реальные станции
     НИЦ ПД ОМЗ Роскосмоса.

2. deliverable_type.typical_processing_level (3 orphans):
   - RADIANCE_L1B: L1 → L1B (businessKey hints L1B)
   - ORTHO_L1C: L1 → L1C (businessKey hints L1C)
   - SURFACE_REFL_L2A: L2 → L2A (businessKey hints L2A)

3. spacecraft.status — already исправлено в commit 5d5b03c (ACTIVE → OPERATIONAL).
   На prod 4 records остались с ACTIVE из старого import — нужен manual PATCH
   через writer API либо TRUNCATE + restart bundle import.

Verified: local seed scan теперь returns 0 orphans across all 6 FK relations.

Prod cleanup TODO (отдельный manual step, требует admin Keycloak token):
- spacecraft.status: 4 records (1999-068A, 2014-016A, 2015-028A, 2024-149E) → OPERATIONAL
- antenna.ground_station: 4 records → новые codes (MOSCOW_OTRADNOE / KHANTY_MANSIYSK_NPC / etc)
- deliverable_type.typical_processing_level: 3 records → L1B/L1C/L2A
2026-05-08 00:01:01 +03:00
Zimin A.N. 9214ee931b feat(geo): auto-derive geometry из data.lat/lon + backfill migration
Записи ground_station и similar dicts имеют lat/lon в data, но
geometryWkt не задан. Spatial filter (AOI) WHERE geometry IS NOT NULL
AND ST_Intersects(...) возвращал 0 records даже когда координаты
есть.

Три места починены:

1. DictionaryRecordService.resolveGeometry — для CRUD через REST.
   Если req.geometryWkt() задан — приоритет (explicit). Иначе
   читаем data.lat/lon (или latitude/longitude/lng), валидируем
   ranges, строим POINT(lon lat) с SRID 4326. Игнор out-of-range
   без throw — не ломаем legacy data.

2. CuodBundleImporter.deriveLatLonGeometry — bundle bypass'ит service,
   reuse'аем ту же логику локально (5 строк inline). Применяется при
   создании НОВЫХ records (existing skipped по businessKey).

3. Liquibase migration 0015-backfill-latlon-geometry — UPDATE existing
   records где geometry IS NULL AND data has lat/lon. Три changeset'а
   для разных aliases (lat/lon, latitude/longitude, lat/lng). Idempotent
   (только NULL→value), rollback supported (NULL обратно).

После deploy: existing 3 ground_station записей (Отрадное и др.) и
spacecraft с lat/lon получат POINT geometry. AOI bbox-фильтр заработает.
2026-05-06 22:39:45 +03:00
Zimin A.N. 5d5b03c421 fix(cuod-bundle): spacecraft seed records — мигрировать статусы на новый FK
Bundle v1.2.1 ввёл x-references на spacecraft_status, но seed records
оставались со старыми enum-значениями (ACTIVE, DECAYED) которых нет
в spacecraft_status.records.json.

Migration:
- ACTIVE (12 records) → OPERATIONAL  (штатная целевая работа)
- DECAYED (2 records) → LOST  (аварийная потеря или внеплановый сход)

Mapping выбран по семантике: ACTIVE = currently working = OPERATIONAL.
DECAYED означает unplanned re-entry = LOST (DECOMMISSIONED это плановый
вывод). После следующего bundle re-import (TRUNCATE+restart) seed
будет проходить x-references validation.
2026-05-06 18:11:23 +03:00
Zimin A.N. 3dddcd1f95 feat(cuod-bundle): spacecraft.status — FK на spacecraft_status
Был inline enum [PLANNED, ACTIVE, INACTIVE, DECAYED, LOST] — но
spacecraft_status справочник существует с нормализованными codes:
PLANNED, LAUNCHED, OPERATIONAL, STANDBY, DEGRADED, DECOMMISSIONED, LOST.

Теперь spacecraft.status = x-references на spacecraft_status.code.
Profit:
- i18n названий статусов (Запланирован/Эксплуатация/...)
- description per status (что значит DEGRADED vs DECOMMISSIONED)
- Расширяемо без schema migration

Breaking change: existing 14 records со status=ACTIVE/DECAYED не пройдут
validation. Решение: full re-import bundle на staging (TRUNCATE +
restart ordinis-app → CuodBundleStartupRunner пересоздаст все).
2026-05-06 17:34:39 +03:00
Zimin A.N. 7b6e2145ea feat(cuod-bundle): v1.2.1 — x-references на 5 FK полях
Демо нового x-references validator (commit 4142678).
До этого FK relationships были только в description как "FK на ...".
Теперь app-level integrity check на CRUD:

- spacecraft.satellite_type_code → satellite_type.code
- consumer.consumer_type → consumer_type.code
- deliverable_type.typical_processing_level → processing_level.code
- orbital_regime.parent_orbit_type → orbit_type.code
- antenna.ground_station → ground_station.code

Effect: POST/PUT записи с invalid FK → 422 ValidationError code
x_references_target_not_found, не silent broken reference.

Bundle version bumped 1.2.0 → 1.2.1 (breaking-change detection
прокатит — schemaVersion полей не менялись, только metadata).
2026-05-06 17:18:28 +03:00
Zimin A.N. b57bc9ef82 feat(cuod-bundle): +33 справочника — bundle v1.2.0
Расширение каталога ЦУОД с 7 до 40 справочников. Покрывает домен ДЗЗ
end-to-end: КА → орбиты → сенсоры → данные → потребители → съёмка → гео →
обработка → связь → контракты.

КА и операторы (5):
- country (ISO 3166-1 — RU/US/CN/EU/etc, 14 записей)
- operator (Роскосмос, NASA, ESA, NOAA, Maxar, Planet, ..., 13)
- spacecraft_status (planned/launched/operational/standby/degraded/decommissioned/lost, 7)
- launch_vehicle (Союз-2/Ангара/Falcon 9/Ariane 6/Long March 2D, 12)
- launch_site (Байконур/Восточный/Cape Canaveral/Куру/Tанегасима, 9)

Орбита (3):
- orbit_type (LEO/SSO/MEO/GEO/HEO/POLAR, 6)
- orbital_regime (SSO AM/PM/dawn-dusk, Молния, Тундра, GEO standard, 6)
- inclination_class (экваториальный → ретроградный, 8)

Сенсоры (5):
- sensor_type (optical PAN/MS, hyperspectral, thermal, SAR, lidar, microwave, 8)
- spectral_band (PAN/B/G/R/RedEdge/NIR/SWIR1/SWIR2/MWIR/TIR/CoastalAerosol/H2O/Cirrus, 13)
- frequency_band (P/L/S/C/X/Ku/K/Ka/UHF, 9)
- polarization (HH/HV/VH/VV, 4)
- resolution_class (VHR/HR/MR/LR/VLR с GSD-границами, 5)

Данные (4):
- coordinate_system (WGS84/Web Mercator/ECEF/ПЗ-90.11/ГСК-2011/СК-42/СК-95/UTM, 9)
- map_projection (Меркатор/UTM/Гаусс-Крюгер/Альберс/Ламберт/Polar Stereo/Sinusoidal/Web Mercator, 8)
- compression_algorithm (NONE/LZW/Deflate/PackBits/JPEG/JPEG2000/ZSTD/LERC, 8)
- qa_flag (Cloud/Shadow/Cirrus/Snow/Water/Saturated/Missing/SunGlint/AtmCorrected/GeomRefined, 10)

Потребители (4):
- consumer (МЧС/Минприроды/Росреестр/Минсельхоз/Росгидромет/Рослесхоз/Минобороны, 7) [scope=INTERNAL]
- consumer_type (министерство/агентство/гос/коммерческая/научная/международная, 7)
- industry_sector (агро/лес/картография/ЧС/экология/метео/нефтегаз/Арктика/...,  14)
- priority_class (P0 emergency → P5 background с SLA в часах, 6)

Съёмка (2):
- imaging_task_type (плановая/заказная/экстренная/strip/stereo/background, 6)
- acquisition_mode (nadir/off-nadir/stripmap/spotlight/scansar/stereo/tristereo, 7)

Гео (3):
- coverage_zone (8 ФО + Арктическая зона, 9)
- climatic_zone (Кёппен: тропики → ледник, 9)
- land_cover_type (ESA WorldCover 11 классов, 11)

Обработка (2):
- atmospheric_correction (Sen2Cor/iCOR/MAJA/6S/MODTRAN/ACOLITE/FLAASH, 7)
- geometric_correction (RPC/RPC+GCP/Ortho-DEM/rigorous-sensor/polynomial-2D, 5)

Связь (2):
- antenna (антенны GS Москва/Ханты/Красноярск/Хабаровск, 4) — FK на ground_station
- modulation_scheme (BPSK/QPSK/8PSK/16APSK/32APSK/64QAM, 6)

Контракты (3) [scope=INTERNAL для contract_type/license_type]:
- contract_type (госзаказ/rate-card/подписка/проектный/обмен, 5)
- license_type (open/CC-BY-NC/internal/single-user/enterprise/restricted/secret, 7)
- deliverable_type (raw/L1B/L1C/L2A/NDVI/DEM/landcover/event-report, 8)

manifest.json: bundle.version → 1.2.0, добавлены все 33 dictionary descriptors
с переводами и scope.

Все JSON-файлы валидируются `python3 -c "json.load"`. Maven build success.
Bundle грузится writer'ом при старте (см. CuodBundleAutoConfiguration).
2026-05-06 00:57:48 +03:00
Zimin A.N. 2fc9b451e4 fix(cuod+docs): Resurs-P/Канопус/Метеор/Электро/Арктика в seed + правильный read-api путь
Альтум-команда обнаружила что /api/v1/dictionaries/{name}/records даёт 500
и spacecraft_codes ссылаются на несуществующий "RESURS-P-3". Оба бага мои:

1. Read-api живёт на /api/v1/{name}/records (БЕЗ /dictionaries/ префикса).
   /dictionaries/* — это admin-write контроллер, у него GET-list нет.
   Fix: docs/integration/altum-imaging-order.md + docs/tech/api-integration.md
   обновлены с правильными URL'ами + примером ответа из live staging.

2. spacecraft seed имел только 4 КА (Terra, Sentinel-1A/2A, Кондор-ФКА),
   а mission.spacecraft_codes ссылался на пустые/RESURS-P-3 — каскад в
   "Заказ съёмки" не работал бы.
   Fix: добавлено 10 российских КА (Ресурс-П 1-4, Канопус-В 1+5, Метеор-М 2+2-2,
   Электро-Л 2, Арктика-М 1) с COSPAR designators как businessKey.

3. mission.records.json + instrument.records.json: spacecraft_codes теперь
   ссылается на реально существующие COSPAR-ключи. Каскад работает:
   instrument MSU-MR → spacecraft [2014-037A, 2019-038A] → Метеор-М № 2 / 2-2.

После redeploy и идемпотентного пересева:
  spacecraft   14 (было 4, +10 РФ)
  mission       9 (existing 9, references фиксированы)
  instrument   11 (было 10, +МСУ-ГСМ Арктики)
2026-05-04 18:27:36 +03:00
Zimin A.N. e284fa85be feat(cuod): + 4 справочника mission, instrument, processing_level, data_format
Греет Альтум-интеграцию (заказ съёмки) и любых других consumer'ов ДЗЗ.
Манифест cuod 1.0.0 → 1.1.0. Existing справочники (spacecraft, satellite_type,
ground_station) не тронуты — их schemaVersion остался 1.0.0.

Схемы:
- mission: программа/семейство КА (Ресурс-П, Канопус-В, Sentinel и т.д.)
- instrument: бортовой сенсор с m2m связью со spacecraft и cascade в
  data_format/processing_level через supported_*_codes
- processing_level: L0...L4 с sort_order для упорядочивания
- data_format: GeoTIFF/NITF/COG/SAFE/... с MIME + расширением

Везде x-id-source=code (auto businessKey), x-unique=true на code.
Поля name/description везде x-localized RU+EN.

Сидинг (35 записей):
- mission: 9 (российские + Sentinel + Terra/Aqua)
- instrument: 10 (Геотон-Л1, КШМСА, ГСА-2, ПСС-В, МСУ-МР, МСУ-ГС, Кондор SAR, MODIS, C-SAR, MSI)
- processing_level: 8 (L0/L1A/L1B/L1C/L2A/L2B/L3/L4)
- data_format: 8 (GEOTIFF, COG, NITF, JPEG2000, NETCDF, HDF5, SAFE, KMZ)

Cross-refs работают: mission.spacecraft_codes и instrument.spacecraft_codes
ссылаются на existing spacecraft businessKeys; instrument.supported_*_codes —
на новые data_format/processing_level. Импортер CuodBundleImporter
идемпотентен — на ребуте поднимет всё, существующее не тронет.
2026-05-04 15:44:38 +03:00
Александр Зимин 0752b3acd6 fix(cuod-bundle): seed records for existing definitions too
Importer бросал early-return для уже существующих definitions, поэтому
records никогда не наполнялись на повторных стартах. Records seed
теперь вызывается всегда (idempotent — skip по businessKey).
2026-05-03 23:38:52 +03:00
Александр Зимин aab7199600 feat(cuod-bundle): seed records auto-import on startup
CuodBundleImporter теперь после создания DictionaryDefinition подгружает
records/<name>.records.json (relative to bundle resourceRoot) и создаёт
DictionaryRecord для каждой записи. Идемпотентно: пропускает business_key,
который уже существует.

Bundle:
- 5 satellite_type (SAR_X_HIRES, OPT_HIRES_VIS, OPT_MIDRES_MS, HYPER, METEO_GEO)
- 4 spacecraft (Terra, Sentinel-1A, Sentinel-2A, Кондор-ФКА №2)
- 3 ground_station (Отрадное, SvalSat, Байконур)

Все с локализованными name/description (ru-RU + en-US), валидируемые
JSON Schema на pre-save. Outbox events RecordCreated отправляются для
projection-writer.
2026-05-03 23:31:51 +03:00
Zimin A.N. ae69a7642b 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 без ошибок
2026-05-03 14:29:43 +03:00
Zimin A.N. a28fd0b352 feat: Maven multimodule scaffolding for Ordinis MDM service
- 10 модулей (domain, validation, events-api, outbox, rest-api, app, read-api, projection-writer, cuod-bundle, migrations)
- Parent pom + BOM с Spring Boot 3.4.2, Spring Cloud 2024.0.0, Vault Config 4.2.0
- ordinis-app: Spring Boot main с Actuator/Prometheus/Logstash JSON encoder/OpenTelemetry/Cloud Config/Vault skeleton
- ordinis-migrations: Liquibase changelogs (dictionary_definitions/records, audit_log, outbox_events, idempotency_keys, EXCLUSION CONSTRAINT через btree_gist)
- Multi-stage Dockerfiles для app и migrations
2026-05-03 00:41:53 +03:00