8.2 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 05-package-export | 01 | execute | 1 |
|
true |
|
|
Purpose: PKG-01/PKG-02/PKG-03 -- Users can download deployment-ready packages in either format with one click.
Output: imptune/api/packages.py with two GET endpoints, registered in main.py, with integration tests.
<execution_context> @C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md @C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/05-package-export/05-RESEARCH.md@imptune/api/scripts.py @imptune/generators/intunewin_builder.py @imptune/generators/script_generator.py @imptune/config.py @imptune/db/models.py @imptune/main.py @tests/conftest.py
From imptune/api/scripts.py:
def _get_printer_and_driver(printer_id: int):
"""Returns (printer, driver, driver_name), None on success
or None, PlainTextResponse on error (404/422)."""
From imptune/generators/script_generator.py:
def render_install(printer_name, ip_address, port_name, driver_name,
inf_filename, duplex_mode, color_mode, paper_size, collate) -> str: ...
def render_uninstall(printer_name, driver_name, port_name) -> str: ...
def render_detect(printer_name) -> str: ...
From imptune/generators/intunewin_builder.py:
def build_intunewin(source_dir: str, setup_file: str, output_path: str) -> None:
"""Build a .intunewin file from source_dir, with setup_file as entry point."""
From imptune/config.py:
DATA_DIR = os.environ.get("DATA_DIR", "/data")
DRIVERS_DIR = str(Path(DATA_DIR) / "drivers")
From imptune/db/models.py:
class Driver(BaseModel):
sha256 = CharField(unique=True, index=True)
original_filename = CharField()
driver_desc = CharField(null=True) # JSON list of driver names
inf_filename = CharField(null=True)
...
class Printer(BaseModel):
name = CharField()
ip_address = CharField()
port_name = CharField()
driver = ForeignKeyField(Driver, null=True, backref="printers")
duplex_mode = CharField(default="OneSided")
color_mode = BooleanField(default=True)
paper_size = CharField(default="A4")
collate = BooleanField(default=True)
...
2. Create `imptune/api/packages.py` with `router = APIRouter(prefix="/printers")`:
**NinjaRMM endpoint** `GET /{printer_id}/packages/ninja`:
- Reuse `_get_printer_and_driver()` pattern from scripts.py (copy the helper into packages.py or import — prefer copy since it's small and keeps the module self-contained)
- Call `render_install(...)` with all printer/driver params
- Build ZIP in-memory with `io.BytesIO` + `zipfile.ZipFile`:
- `{safe_name}/install.ps1` with rendered script
- `{safe_name}/drivers/{member}` for each file in the driver ZIP on disk
- `safe_name = printer.name.replace(" ", "_")`
- Return `Response(content=buf.getvalue(), media_type="application/zip", headers={"Content-Disposition": f'attachment; filename="{safe_name}_ninja.zip"'})`
**Intunewin endpoint** `GET /{printer_id}/packages/intunewin`:
- Same printer/driver validation via `_get_printer_and_driver()`
- Use `tempfile.TemporaryDirectory(prefix="imptune_")` as context manager (auto-cleanup, per RESEARCH pitfall 1)
- Write `install.ps1`, `uninstall.ps1`, `detect.ps1` into tmpdir
- Extract driver ZIP contents into `tmpdir/drivers/`
- Call `build_intunewin(tmpdir, "install.ps1", os.path.join(tmpdir, "out.intunewin"))`
- Read output file bytes and return as `Response(content=..., media_type="application/octet-stream", headers={"Content-Disposition": ...})`
- Check driver file exists on disk before proceeding (per RESEARCH pitfall 3), return 422 if missing
3. Register router in `imptune/main.py`:
- Add `from imptune.api import packages` to imports
- Add `app.include_router(packages.router)` after scripts router
4. Run tests GREEN.
<success_criteria>
- GET /printers/{id}/packages/ninja returns downloadable ZIP with install.ps1 + driver files
- GET /printers/{id}/packages/intunewin returns downloadable .intunewin package
- Both endpoints return proper error codes for invalid requests
- Full test suite green </success_criteria>