Files
shopdb-flask/plugins/printedparts
cproudlock 7f6aaf2e64 Gate the USB kiosk the same way, and make an unreachable setting a test failure
The USB kiosk had a feature flag and nothing else. A flag is a decision, not a
credential: with the kiosk on, anyone who could reach the app could move a drive
between checked-out and checked-in and sign it with a badge they guessed. Same
reasoning that closed the parts kiosk, smaller blast radius - which is why
Darkwing did not raise it and why it was worth asking about anyway.

Both kiosks now share one mechanism. The device token moved out of the
printedparts api wrapper into a request interceptor that attaches X-Kiosk-Token
by URL prefix, with one localStorage key per kiosk - they are separate screens
in separate rooms, and a shared token would mean provisioning one grants the
other. claimKioskToken() is shared too, so both views take the token from a
?token= URL once and strip it from the address bar identically.

Then the part that matters more than either fix. I told you to set the new
settings in the UI, and that was impossible: every plugin settings page carries
a hardcoded key list, and nothing had added them. printedparts_kiosk_enabled,
printedparts_kiosk_token and usb_kiosk_token were declared, seeded and read
correctly, and rendered by no page - changeable only by writing SQL.

tests/test_plugin_settings_reachable.py now fails when any declared setting
appears nowhere in its plugin's frontend. It found seven MORE that were already
in that state, all pre-existing:

  collector_corporate_ranges          computers_configpolicies_enabled
  computers_machinelink_alerts        computers_quietreporthours
  geenforce_reportstaleminutes        printers_dashboardpercent
  warranty_expiringdays

All seven now have fields. I first parked them in a KNOWN_GAPS ratchet, then
fixed them and deleted the ratchet - a backlog list in a new check is a way of
agreeing the check does not apply yet. There is no backlog; the check is
absolute.

Two of my own mistakes on the way, both caught by tests rather than by reading:
the blanket rename of the USB guard also rewrote the call inside _kiosk_open, so
it called itself until the stack ran out; and the first version of the reachable
check skipped all 17 plugins because it looked for get_settings() when the
method is get_settings_defaults() - it passed by testing nothing, which is the
exact shape it exists to catch.

Suite 2336, frontend 316, build clean.

Scope: product
2026-09-02 18:08:34 -04:00
..

Printedparts plugin

3D-printed parts inventory + kiosk checkout

This plugin was generated by flask plugin new printedparts. It satisfies the framework contract out of the box. Replace the example model and routes with your domain.

What's here

  • plugin.py - the PrintedpartsPlugin class extending BasePlugin. Edit init_app for custom setup, on_install to seed reference data.
  • models/printedparts.py - example Asset extension table. Replace examplefield with your domain fields.
  • api/routes.py - example list and detail endpoints. Add CRUD as needed.
  • schemas/__init__.py - marshmallow schema stub for request/response validation.
  • tests/test_plugin.py - smoke tests asserting contract compliance.
  • manifest.json - plugin metadata. Bump version on changes; keep core_version range broad.

Common edits

You want to... Do this
Add a hook (search, navigation, dashboard widget) Override the method in PrintedpartsPlugin. See the PLUGIN-HOOKS wiki page.
Accept external collector data Override get_collector_schema() to return a JSON Schema. See ADR-006.
Add another model Create models/<other>.py, export it in models/__init__.py, return it in get_models().
Add a CLI command Override get_cli_commands() returning a list of Click commands.

Frontend

Vue components for this plugin live under frontend/src/views/printedparts/ (per project convention). Backend scaffolding does not generate frontend yet; copy from an existing plugin's view files (e.g., frontend/src/views/network/) as a starting point.

Install and run

flask plugin install printedparts
flask db migrate -m "Add printedparts plugin tables"
flask db upgrade
pytest plugins/printedparts/tests/

References

  • the PLUGIN-HOOKS wiki page - canonical hook reference
  • the PLUGIN-QUICKSTART wiki page - 30-minute walkthrough
  • migrations/adr/ADR-001-asset-as-platform-contract.md - the platform contract
  • migrations/adr/ADR-002-plugin-versioning.md - versioning rules

Why the kiosk take endpoint is unauthenticated

POST /api/printedparts/kiosk/take is the product's first open WRITE (every other kiosk endpoint is a read). Accepted deliberately, against the criteria in docs/proposals/printedparts-plugin.md:

  1. Decrement-only: it can reduce stock of an active item, nothing else.
  2. Fully attributed: it refuses to act without a badge that resolves under the site policy; every action lands in the ledger with SSO + name + time.
  3. Bounded blast radius: worst case is stock counts driven low - visible in the ledger and reversible with an adjust.
  4. Physically rate-limited: it serves a touch screen on the shop floor; nothing enumerable, nothing worth scraping.

Any future open-write endpoint must clear the same bar.