Skip to content

feat: add JSON format versioning support for converter#88

Closed
fank wants to merge 8 commits intomainfrom
feature/json-format-versioning
Closed

feat: add JSON format versioning support for converter#88
fank wants to merge 8 commits intomainfrom
feature/json-format-versioning

Conversation

@fank
Copy link
Member

@fank fank commented Jan 30, 2026

Summary

  • Add versioned parser system to handle future JSON format changes
  • Converter now detects input format version and uses appropriate parser
  • Track JSON format version in database and expose via API

Changes

Storage layer (internal/storage/):

  • version.go - JSONVersion type, constants, and DetectJSONVersion()
  • parser.go - Parser interface and registry (RegisterParser, GetParser)
  • parser_v1.go - V1 parser implementing Parser interface
  • converter.go - Refactored to use versioned parser registry
  • VERSIONING.md - Documentation for adding new versions

Server layer (internal/server/):

  • operation.go - Added json_format_version column (migration v4)
  • handler.go - FormatInfo now includes JSONFormatVersion

How it works

When the JSON format changes in the future:

  1. Add new version constant (e.g., JSONVersionV2)
  2. Update DetectJSONVersion() to recognize new format
  3. Create new parser file (e.g., parser_v2.go)
  4. Existing V1 recordings continue to work

Test plan

  • All existing tests pass
  • New tests for version detection, parser registry, and V1 parser
  • Database migration tested
  • API endpoint returns JSONFormatVersion

fank added 8 commits January 30, 2026 19:35
Introduce JSONVersion type and constants to track input format versions.
This prepares for versioned parsing when the JSON format changes.
DetectJSONVersion analyzes JSON structure to determine which parser
version should handle the data. Currently detects V1 by required fields.
Introduce Parser interface for version-specific JSON parsing.
Registry pattern allows adding new format versions without modifying
existing code.
Move JSON parsing logic to dedicated ParserV1 implementing Parser interface.
This preserves existing behavior while enabling future format versions.
Replace hardcoded parseJSONData with parseJSONDataVersioned that detects
the input format version and delegates to the appropriate parser.
Falls back to V1 for unrecognized formats.
Track which JSON format version was used for each recording.
Enables future re-conversion when format changes.
Clients can now check which JSON format version was used to create
each recording.
Document how to add new format versions when the OCAP JSON schema changes.
@github-actions
Copy link

Merging this branch will increase overall coverage

Impacted Packages Coverage Δ 🤖
github.com/OCAP2/web/internal/server 44.44% (+0.25%) 👍
github.com/OCAP2/web/internal/storage 59.69% (+0.76%) 👍

Coverage by file

Changed files (no unit tests)

Changed File Coverage Δ Total Covered Missed 🤖
github.com/OCAP2/web/internal/server/handler.go 40.31% (ø) 712 287 425
github.com/OCAP2/web/internal/server/operation.go 50.00% (+0.45%) 482 (+40) 241 (+22) 241 (+18) 👍
github.com/OCAP2/web/internal/storage/converter.go 77.38% (+11.69%) 526 (-328) 407 (-154) 119 (-174) 🎉
github.com/OCAP2/web/internal/storage/flatbuffers.go 54.17% (ø) 923 500 423
github.com/OCAP2/web/internal/storage/parser.go 81.25% (+81.25%) 16 (+16) 13 (+13) 3 (+3) 🌟
github.com/OCAP2/web/internal/storage/parser_v1.go 49.60% (+49.60%) 375 (+375) 186 (+186) 189 (+189) 🌟
github.com/OCAP2/web/internal/storage/version.go 78.43% (+78.43%) 51 (+51) 40 (+40) 11 (+11) 🌟

Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code.

Changed unit test files

  • github.com/OCAP2/web/internal/server/handler_test.go
  • github.com/OCAP2/web/internal/server/operation_test.go
  • github.com/OCAP2/web/internal/storage/converter_test.go
  • github.com/OCAP2/web/internal/storage/parser_test.go
  • github.com/OCAP2/web/internal/storage/version_test.go

@fank
Copy link
Member Author

fank commented Jan 31, 2026

Closing in favor of a more comprehensive end-to-end versioning approach. See new plan in docs/plans/2026-01-30-end-to-end-versioning.md

@fank fank closed this Jan 31, 2026
@fank fank deleted the feature/json-format-versioning branch January 31, 2026 09:46
fank added a commit that referenced this pull request Jan 31, 2026
Comprehensive versioning strategy covering:
- JSON input format versioning
- Protobuf/FlatBuffers schema versioning
- Versioned writers and readers
- Database schema_version tracking
- API schemaVersion in responses
- Frontend versioned loaders

Replaces partial implementation from closed PR #88.
fank added a commit that referenced this pull request Jan 31, 2026
* docs: add end-to-end format versioning plan

Comprehensive versioning strategy covering:
- JSON input format versioning
- Protobuf/FlatBuffers schema versioning
- Versioned writers and readers
- Database schema_version tracking
- API schemaVersion in responses
- Frontend versioned loaders

Replaces partial implementation from closed PR #88.

* feat(storage): add unified version types for end-to-end versioning

* refactor(schemas): move protobuf schema to v1 directory

Move protobuf schema files to versioned directory structure:
- pkg/schemas/protobuf/ocap.proto -> pkg/schemas/protobuf/v1/ocap.proto
- Update package from `ocap` to `ocap.v1`
- Update go_package to `github.com/OCAP2/web/pkg/schemas/protobuf/v1`
- Regenerate Go code with protoc

Part of end-to-end versioning system (Task 2.1).

* refactor(schemas): move flatbuffers schema to v1 directory

- Create pkg/schemas/flatbuffers/v1/ directory structure
- Move ocap.fbs to v1/ and update namespace from ocap.fb to ocap.v1.fb
- Regenerate FlatBuffers Go code in v1/generated/
- Delete old fbs and generated/ from pkg/schemas/flatbuffers/

Note: Build will fail until imports are updated in Task 2.3

* refactor(storage): update imports to use versioned schema packages

Update all Go files that import protobuf and flatbuffers schemas to use
the new v1 versioned paths. Changes pb -> pbv1 and fb -> fbv1 aliases.

* feat(storage): add Parser interface with schema-agnostic types

Add ParseResult intermediate representation for JSON parsing that is
schema-agnostic. This allows different input JSON versions to be parsed
by different Parser implementations and then written to any output format.

- ParseResult: intermediate data structure with manifest, entities, events,
  markers, times, and position data
- Parser interface: Version() and Parse() methods
- Parser registry: RegisterParser/GetParser for version-specific parsers
- Reuses existing EntityDef and Event types from engine.go

* feat(storage): add ParserV1 for JSON v1 format

Implement ParserV1 that parses the current Arma 3 JSON format into
schema-agnostic types (EntityDef, Event, MarkerDef, TimeSample,
EntityPositionData). This is part of the versioned parser/writer
architecture that enables future format evolution.

* feat(storage): add Writer interface with version prefix helpers

Add Writer interface as the counterpart to Parser for writing ParseResult
to specific formats (protobuf/flatbuffers) with schema versioning.

- Writer interface with Version(), Format(), WriteManifest(), WriteChunks()
- Writer registry with RegisterWriter() and GetWriter()
- WriteVersionPrefix() writes 4-byte little-endian version prefix
- ReadVersionPrefix() reads version prefix from files
- Comprehensive tests for registry and version prefix round-trip

* feat(storage): add ProtobufWriterV1 for v1 protobuf format

Implement Writer interface for protobuf v1 format that:
- Writes 4-byte version prefix before protobuf data
- Converts schema-agnostic ParseResult to protobuf messages
- Writes manifest.pb with version prefix
- Writes chunk files with version prefix

* feat(storage): add FlatBuffersWriterV1 for v1 flatbuffers format

Implements the Writer interface for FlatBuffers v1 format with 4-byte
version prefix. Supports writing manifest with entities, events, markers,
and time samples, plus chunked frame data with entity states.

* feat(storage): add version prefix support to protobuf engine

Update ProtobufEngine to handle files with optional 4-byte version prefix.
This provides backward compatibility with legacy files while supporting
the new versioned format written by ProtobufWriterV1.

The version detection uses a heuristic: if bytes 2-4 are all zero, the
first 4 bytes are interpreted as a little-endian version number.
Otherwise, the file is treated as legacy format without version prefix.

* feat(storage): add version prefix support to flatbuffers engine

Add readVersionedData method to FlatBuffersEngine for handling files
with 4-byte version prefix. This provides backward compatibility with
both versioned and legacy FlatBuffers files.

Key changes:
- Add readVersionedData method using FlatBuffers-specific heuristic
- Update GetManifest and GetChunk to use versioned data reading
- Add tests for versioned and legacy file handling

The heuristic distinguishes version prefixes from FlatBuffers root
offsets by checking: first byte < 16 AND bytes 2-4 are zero (version
prefix) vs first byte >= 16 (FlatBuffers root offset).

* refactor: use versioned parser/writer in converter

Refactor converter.go to use the versioned parser and writer components:
- Use DetectJSONInputVersion() to detect input JSON version
- Use GetParser() to get the appropriate versioned parser
- Use GetWriter() to get the appropriate versioned writer
- Add format parameter to Convert() for output format selection
- Remove duplicated parsing logic (now in parser_v1.go)
- Remove duplicated writing logic (now in writer_protobuf_v1.go/writer_flatbuffers_v1.go)
- Update protobuf.go and flatbuffers.go Convert methods to pass format
- Remove unused helper functions from flatbuffers.go
- Update tests to handle 4-byte version prefix in output files

* feat: complete end-to-end versioning implementation

- Add schema_version column to database (migration v4)
- Add SchemaVersion to FormatInfo API response
- Add UpdateSchemaVersion to OperationRepo interface
- Create frontend versioned loader system (LoaderV1, LoaderRegistry)
- Update ocap.js to use versioned loaders from registry
- Update ChunkManager to use versioned loader for chunk decoding
- Mark versioning plan as implemented

* Revert "docs: add end-to-end format versioning plan"

This reverts commit e61e5c2.

* feat: end-to-end format versioning

Implements comprehensive versioning throughout the entire data pipeline,
allowing format changes to evolve independently while maintaining
backwards compatibility.

Version flow: JSON Input → Parser → Writer → .pb/.fb → Reader → API → UI Loader

Backend:
- Add SchemaVersion/JSONInputVersion types with detection
- Move schemas to versioned directories (protobuf/v1, flatbuffers/v1)
- Add Parser interface with registry (ParserV1 implementation)
- Add Writer interface with registry (ProtobufWriterV1, FlatBuffersWriterV1)
- Add 4-byte version prefix to binary files
- Add schema_version column to database (migration v4)
- Add schemaVersion to FormatInfo API response

Frontend:
- Add LoaderRegistry for versioned loaders
- Add LoaderV1 wrapping existing decoders
- Update ocap.js and ChunkManager to use versioned loaders

* fix: CLI convert command now preserves .json in output path

The convertSingleFile function was stripping both .gz and .json
extensions, but database filenames include .json. This caused a
mismatch where converted files couldn't be found by the API.

Now only strips .gz to match the database filename format.

* feat: CLI convert updates database when operation exists

When using `convert --input`, the CLI now:
- Looks up operation by filename in database
- Updates mission duration from manifest
- Updates storage format, conversion status, and schema version

Also adds GetByFilename method to RepoOperation.

* refactor: CLI uses worker.ConvertOne for DB operations

When converting a file that exists in the database, the CLI now uses
the same worker.ConvertOne() method as the background worker, ensuring
identical behavior for both paths.

Standalone conversions (no DB entry) still work without DB updates.

* test: add tests for GetByFilename and UpdateSchemaVersion

* test: add test for CLI convert with database entry

* test: add tests for protobuf ChunkCount and readVersionedData error paths

* test: add SchemaVersion assertions to handler tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant