Skip to content

cisagov/parsnip-frontend

Repository files navigation

Parsnip Frontend

Angular application for authoring Parsnip IL. Talks to the Parsnip backend via a relative /api/* path so the bundle is deployment-agnostic.

This repo is self-contained — it has its own docker-compose.yml for standalone work, and it also slots in as a submodule of the parsnip superproject when you want to run it alongside the backend and compiler.

App shape

  • src/app/pages/ — routed pages. home, overview, parsers (list), components (tabbed Enums / Bitfields / Objects / Switches, parser-scoped), review, export, import, compile.
  • src/app/features/components/ — entity feature folders. Each folder has a list, expanded-view, detail, form-modal, bulk-import-modal, and delete-modal where applicable: enums/, bitfields/, objects/, switches/, parsers/ (with user-type-form-modal for configuration CustomFieldTypes).
  • src/app/shared/dependency-form-modal, scope-modal, parser-selector, modal, icon, toast.
  • src/app/services/ — id-based API clients (object.service, switch.service, enum.service, bitfield.service, configuration.service, parser-api.service), plus parser-context.service (id-only) and toast.services.
  • src/app/models/ — DTOs aligned with backend Scalar spec.
  • src/app/layout/ — header/nav chrome.
  • src/styles.scss — shared .data-table + .icon-btn styles used across every list view.

Routes

Handled by src/app/app.routes.ts:

  • /parsers, /parsers/:parserId
  • /components, /components/:parserId
  • /components/:parserId/{enums|bitfields|objects|switches}/:id
  • /review, /export, /import, /compile, /overview, /

Entity detail routes use integer ids, not names.

Authoring UX

Two authoring surfaces. Single-entity forms are the default path; bulk-import is a power-user shortcut.

  • Single-entity forms — one modal per entity. Object-field modal carries size selects for uint / int / bytes / float (f32/f64) / addr (IPv4/IPv6) / user-defined custom types, plus use_custom_scope + reference-scope controls on both the scalar-reference and list-element-reference branches. Switch form modal carries per-option inputs with minus, element type for list actions, full default-action editing, and edit-mode hydration of options/actions/enums/deps. Parser-detail exposes protocol description, conversion/signature file upload, user-type CRUD, port tuples, and scopes.
  • Bulk-import modals — objects (objects-field-bulk-import-modal), switches, bitfield-fields, enums. JSON paste of the full IL shape (inputs, conditional, until, references, elementType). Forward references are allowed; export preflight is the strict gate.
  • Edit-reopen hydration — modals forkJoin their hydration GETs and gate submit behind a hydrating signal so a pre-hydration save can't clobber typed values with stale data.
  • Atomic saves — object-field advanced children save through POST /object_fields/<id>/upsert-advanced; switch edits save through POST /switches/<id>/upsert-tree. No client-side clear-then-rebuild.
  • Import / Export/import uploads a .pil bundle as a new parser. /export downloads a single .pil package via POST /parsers/{id}/generate_il with inline preflight diagnostics.
  • Compile/compile uploads a .pil to POST /parsers/compile, which the backend proxies to the compiler microservice (parsnip compiler + spicyz). Returns the full artifact tree (source + .hlto) as a zipped bundle that auto-downloads on success, plus collapsible panels for the compiler and spicyz logs. Failure surfaces as an inline 422 with both logs open.

API routing

All HTTP calls use the relative base path /api. Two surfaces translate that to the backend:

  • Docker / production: nginx.conf proxies /api/* → http://backend:5000/* inside the compose network. The prefix is rewritten before proxy so /api/parsers reaches the backend as /parsers. No CORS dance, same-origin.
  • Local dev (bunx ng serve): proxy.conf.json (wired through angular.jsonserve.options.proxyConfig) proxies /api/* → http://localhost:5000/*. Start the backend on port 5000 first.

The upshot: frontend code never constructs absolute backend URLs. Change the backend host by adjusting nginx/proxy config, not application code.

Docker

The production path is Dockerfile → multi-stage build with oven/bun:1-alpine (bun install + bun run build) → nginx:alpine serving dist/parsnip/browser on port 80. There is no live dev server in the container path.

Standalone (from parsnip-frontend/)

docker compose up --build

Open http://localhost:4200. The standalone compose only starts the frontend container; for a working UI you need a backend reachable at the Docker network alias backend on port 5000. For a full local stack, use the superproject's root-level compose instead.

Root-level (from the parsnip superproject)

docker compose up --build

Brings up frontend, backend, and database.

Rebuild hygiene

Source is baked at build time. docker compose restart and up without --build will serve the previous bundle even if source has changed. After editing source:

docker compose build --no-cache frontend && docker compose up -d --no-deps frontend

Verify the new bundle is actually being served by grepping the served JS for a marker from the new code:

docker exec parsnip-frontend sh -c 'grep -lo "<marker-string>" /usr/share/nginx/html/*.js'

Local dev server

Requires the backend running on port 5000 (the proxy config assumes localhost).

bun install
bunx ng serve

Open http://localhost:4200/. The proxy handles /api/* routing; source edits hot-reload.

Building

ng build

Artifacts land in dist/.

Tests

ng test        # Karma unit tests
ng e2e         # no default e2e framework; choose one when adding tests

Additional resources

Angular CLI Overview and Command Reference.

About

Submodule of cisagov/parsnip. Hosts a web application for building parsers.

Resources

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors