Skip to content

Ab/initial data model work - add z9/open and z9/flex community#22

Open
z9security wants to merge 32 commits into
Access-Grid:ab/initial-data-model-workfrom
z9security:ab/initial-data-model-work
Open

Ab/initial data model work - add z9/open and z9/flex community#22
z9security wants to merge 32 commits into
Access-Grid:ab/initial-data-model-workfrom
z9security:ab/initial-data-model-work

Conversation

@z9security
Copy link
Copy Markdown

No description provided.

z9security and others added 30 commits April 16, 2026 13:27
…ture

- Fix broken FK: credential_format_parity_bit_ranges now references
  credential_format_parity_bits (was pointing to nonexistent table)
- Add has_many associations to all parent models (Building, Sector,
  AccessController, EntryWay, Group, Person, CredentialType, etc.)
- Fix Sector parent to be optional with self-referential children
- Add presence validations on name fields across all named models
- Set up API controller base class (ActionController::API with auth hook)
- Add /api/health endpoint with integration test
- Add Z9/Flex Community API integration plan document
- Update .ruby-version to 3.2.9 (matching installed rbenv version)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- POST /authenticate accepts {username, password, apiClientType}
- Returns {authenticated, sessionToken, softwareVersion, apiVersion,
  timeZone, softwareVersionTimestamp} per swagger AuthenticateResult
- Failed auth returns {authenticated: false} (no 401, matches swagger)
- Auth enforcement via sessionToken header (swagger APIKeyAuth scheme)
  on all Api::BaseController endpoints, returns 401 for missing/expired
- User model with bcrypt has_secure_password, unique username
- ApiSession model with auto-generated token, expiry, active scope
- Enable bcrypt gem
- 23 tests, 50 assertions (6 User, 8 ApiSession, 6 auth integration,
  3 auth enforcement)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- GET /controller/list with DevListResponse pagination (offset, max, count)
- POST /controller/save creates AccessController, returns {instance: Dev}
- POST /controller/update/{id} by unid (integer) or uuid (string)
- POST /controller/delete/{id} by unid or uuid
- ControllerTranslator maps AccessController <-> Flex Dev JSON (devType=1,
  ObjRefs for sector parent and entry_way children)
- Add uuid column to access_controllers with auto-generation
- All endpoints enforce sessionToken auth (401 without)
- 47 tests, 104 assertions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- GET/POST /door/* endpoints (EntryWay, devType=5)
- GET/POST /credReader/* endpoints (Reader, devType=4)
- GET/POST /sensor/* endpoints (Sensor, devType=2)
- DoorTranslator, CredReaderTranslator, SensorTranslator
- Extract HasUuid concern (shared by 4 models)
- Extract DevTranslatorBase with shared obj_ref, base_dev_fields
- Extract find_by_id_or_uuid and paginate into BaseController
- Add uuid column to entry_ways, readers, sensors
- 80 tests, 181 assertions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Unify AccessController, EntryWay, Reader, Sensor into a single
devices table using Rails STI (IoController, Door, CredReader, Sensor
< Device). This gives all device types a shared ID space matching
the Z9/Flex Dev type hierarchy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…wagger

Add Flex fields (name, uuid, enabled, effective, expires, cardPin JSON) to
credentials table and (name, uuid, priority, cardPinTemplate JSON) to
credential_types table. Make person_id and credential_type_id nullable since
Flex Cred doesn't require them. Add translators, API controllers, routes,
and 59 new tests (149 total, all passing).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…yout CRUD

Add card data format definitions (Wiegand bit encoding) and data layout
endpoints per Z9/Flex Community swagger. BinaryFormat stores polymorphic
elements array (Static/Parity/Field subtypes) as JSON column on
credential_formats. BasicDataLayout references a DataFormat via ObjRef.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add DoorAccessPriv endpoints (list/save/update/delete) mapped to
AccessRuleSet model. Elements stored in a proper join table
(door_access_priv_elements) with FK references to access_rule_sets
and devices, rather than a JSON column.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…dule wiring

Add Schedule, ScheduleElement, HolidayType, HolidayCalendar, Holiday models
with join tables for HolType references. SchedTranslator maps day-of-week
booleans to/from Flex integer arrays. Wire schedule ObjRef into
DoorAccessPrivElement's schedRestriction (was hardcoded nil). 383 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Event model with JSON columns for snapshot refs (evtDevRef,
evtControllerRef, evtCredRef, evtSchedRef, evtModifiers). Implement
GET /evt/list (with order asc/desc, pagination) and GET /evt/show/{id}.
No save/update/delete -- events are read-only. 407 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ands

Add Actuator (devType 3) and NodeDev (devType 0) STI device types with full
CRUD endpoints. Add EncryptionKey model/table for db-based keystore with CRUD.
Add doorModeChange and doorMomentaryUnlock command endpoints (GET, resolve
device by unid/uuid). 467 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements session logout per swagger spec. Requires valid sessionToken,
destroys the ApiSession, and invalidates the token. 470 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add 13 swagger Dev fields to devices table (address, port, devMod,
  devPlatform, devUse, externalId, timeZone, etc.)
- Update DevTranslatorBase with all Dev fields in to_flex/from_flex,
  plus translator_for() and class_for_dev_type() dispatch methods
- Add polymorphic /dev/* endpoints (list/save/update/delete) using
  devType discriminator to route to correct STI subclass and translator
- Replace boilerplate README with project docs and full gap TODO list
- 514 tests passing (44 new)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…chestrator

- Add SpCoreServer (lib/spcore_server.rb): protobuf TCP server speaking
  Z9 Open Community Protocol with custom framing (magic + length + body)
- Add DbChangeBuilder (lib/db_change_builder.rb): serializes Rails models
  to protobuf DbChange messages for sending to Aporta
- Add proto bindings (lib/proto/): Ruby protobuf files generated from
  z9open-community v1.0.13 tag matching Aporta's NuGet package
- Add E2E rake orchestrator (lib/tasks/e2e.rake): starts PD sim, SpCoreServer,
  and Aporta, sends config via DbChange, simulates card reads, verifies
  ACCESS_GRANTED/DENIED events through the full pipeline
- Add unit tests for SpCoreServer and DbChangeBuilder
- Add google-protobuf gem dependency
- Update README with E2E architecture docs and test commands

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CredPrivBinding: migration, model, translator (to_flex/save_priv_bindings),
controller (save/update), and DbChangeBuilder proto serialization. Binds
credentials to access rule sets with optional schedule restriction and
precision door access.

SpCoreServer: auto-sync lifecycle (builds full sync from DB via
DbChangeBuilder on Aporta connect), event persistence to Event table,
proto enum resolution, and db_change_modifier callback for API gaps.

E2E test rewritten to exercise the full pipeline: REST API creates all
entities -> DbChangeBuilder serializes to proto -> SpCoreServer auto-syncs
to Aporta -> card swipe -> access decision events flow back -> persisted
to DB -> verified via /evt/list REST API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…s done

E2E test flow now documents the actual pipeline: REST API creates entities,
SpCoreServer auto-syncs via DbChangeBuilder, events verified via /evt/list.
CredReaderConfig TODO notes the db_change_modifier workaround.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add dev_config JSON column to devices table. Implement config storage,
REST API serialization, and proto serialization for ControllerConfig,
CredReaderConfig (+ commType, tamperType, ledType, serialPortAddress),
SensorConfig (+ invert), ActuatorConfig (+ invert), and NodeDevConfig.
Remove E2E db_change_modifier workaround -- pipeline is now 100% clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add door_access_modifiers JSON column to credentials. Implement storage,
REST API serialization, and proto serialization for DoorAccessModifiers.
Move DoorConfig to upstream schema gaps section in README alongside Hol.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Both element_to_flex methods now emit unid: el.id in their output,
matching the swagger schema for DoorAccessPrivElement and SchedElement.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tocol

externalId was missing from the AccessRuleSet model and translator.
kind, frequency, and protocol on CredentialType were not in the
community swagger spec and have been removed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces record.class.name with FlexTypeNames.for(record) in all
obj_ref methods. Maps: IoController->Controller, Schedule->Sched,
CredentialType->CredTemplate, HolidayType->HolType,
HolidayCalendar->HolCal, CredentialFormat->DataFormat,
AccessRuleSet->DoorAccessPriv.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hol entries ARE in the community DbChange proto (fields 155-157).
The gap is in real-bs DbChangeBuilder, not upstream. Corrected the
README and the misleading comment in db_change_builder.rb.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Individual holidays (Hol) are now serialized into DbChange for
download to Aporta. Includes date, holTypesUnid, holCalUnid,
allHolTypes, numDays, repeat, numYearsRepeat, preserveSchedDay.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…evConfig; consolidate migrations

Serve version (default 0), tag, and commFamily across all translator
to_flex/from_flex methods. DevConfig subtypes now include unid (device id)
and version. Optimistic locking behavior is a separate TODO.

Consolidate 34 incremental migrations into a single create_all_tables
migration since this code has never been deployed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename version_counter to lock_version across migration, schema, and all
translators. Rails automatically increments lock_version on update and
raises StaleObjectError when the WHERE clause misses.

BaseController rescue_from returns 409 Conflict on stale writes.
update_with_lock helper sets lock_version from client's version field
before update, matching Hibernate @Version semantics. All 16 controller
update actions use update_with_lock. Updates without a version field
skip the locking check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add lock_version and tag to all 10 DbChangeBuilder proto methods (Dev,
Cred, CredTemplate, DataFormat, DataLayout, Priv, Sched, HolType, Hol,
HolCal). Remove the now-unused db_change_modifier callback infrastructure
from SpCoreServer -- DevConfig is properly implemented via the API so the
OSDP patch workaround is no longer needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…oints, DevStateRecord, externalDevMod

- Implement DoorConfig: full translator, DbChangeBuilder proto serialization with DoorMode/DoorModeStaticState
- Add show/{id} GET endpoints for all 20 entity types (19 new + evt already existed)
- Add DevStateRecord list endpoint (read-only projection from Device table)
- Add Dev.externalDevModText and Dev.externalDevModId fields (migration + translator + proto)
- Update README: 102 of 102 swagger endpoints implemented

684 tests, 0 failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…Config

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
z9security and others added 2 commits April 17, 2026 12:13
commFamily was removed from the community swagger (the CommFamily enum was
never part of the community profile). Remove it from base_dev_fields and
base_from_flex; leave the DB column in place.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…olderType

Replace Group model with CredHolderType (with uuid, version, tag) to align
with the Z9/Flex community profile. Replace Person's metadata JSON column
with custom_text_0 through custom_text_7 string columns matching CustomData.
Add uuid, lock_version, tag, enabled to Person. Make cred_holder_type FK
optional.

New endpoints: credHolder (list/show/save/update/delete) and credHolderType
(list/show/save/update/delete). CredHolderTranslator maps Person to Flex
CredHolder format including emails/phones as arrays and customData.

All 736 tests pass including E2E.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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