Documentation
¶
Overview ¶
Package monolith reads a consolidated Jamf Pro OpenAPI document and writes it into specs/ as a single normalised file, plus the App Installer subtree that no consolidated document carries.
It used to *split* that document into 165 per-resource files, and then the parser named each resource after the file it landed in. Those filenames were upstream's jss module names, appeared in no spec, and silently decided the command name, the endpoint-version family, whether a `-preview` tag reached a command, and whether the splitter could delete the file. Resource identity now comes from the URL paths (parser.ParseMonolith), so the split carried naming rather than information and the routing it needed is gone: the layout scan, the tag-derived fallback, the shared/exclusive component partitioning and the filename protection list with it.
What survives is document handling — fetching, example coercion and deterministic YAML output. The last of those is the one real benefit the 165 files gave: a 2 MB document written as sorted YAML still produces a readable `git diff` on a spec ingest.
Index ¶
Constants ¶
const AppInstallerSubtree = "/v1/app-installers"
AppInstallerSubtree is the path subtree ExtractSubtree derives the App Installer spec from, and AppInstallerSpecs routes it into one file.
One file, because a filename no longer names anything. It used to be four: one tag covers all 23 operations upstream, so tag routing could not separate the families and the four filenames were how `app-installers`, `app-installer-titles`, `app-installer-deployments` and `app-installer-global-settings` got their names. Resource identity now comes from the paths, which separate the four families on their own — and better, since `/v1/app-installers/titles` says so more plainly than a filename does.
This subtree is here at all because App Installers sits under hiddenapi/ in jamf/jss: no consolidated /api/schema/ document carries it, so the gateway's published Pro API spec is the only thing that describes it.
const NormalisedSpecFile = "JamfProAPI.yaml"
NormalisedSpecFile is the filename Normalise writes the consolidated Jamf Pro API document to.
Variables ¶
var AppInstallerSpecs = []SubtreeSpec{
{
Prefix: "/v1/app-installers",
Filename: "AppInstallers.yaml",
Title: "Jamf Pro API - App Installers",
Description: "App Installers: the feature probe, the Jamf App Catalog of available titles and their versions, deployments to computers with their per-computer installation state, and the global settings controlling end-user notifications and deployment process controls.",
},
}
Functions ¶
func ExtractSubtree ¶ added in v1.28.0
func ExtractSubtree(source, specsDir, subtree string, specs []SubtreeSpec) ([]string, []string, error)
ExtractSubtree derives per-resource spec files for one subtree of a consolidated Jamf Pro OpenAPI document, leaving every other file in specsDir alone.
This exists because one Jamf Pro surface is published only on the gateway. App Installers sits under hiddenapi/ in jamf/jss, so the jss bundle and the instance's own /api/schema/ monolith both exclude it, and the specs this repo generated its commands from were reverse-engineered for exactly that reason. public-apis-oas#430 published all 23 operations into the gateway's Jamf Pro API spec on 2026-09-03, which is the SDK's api/pro_api.json — the same file gateway coverage is derived from. So the authoritative spec now arrives with the coverage sync, and these files are derived from it rather than maintained by hand.
It is deliberately not Split. Split owns the whole of specsDir: it wipes every root *.yaml, routes every path in the document, and partitions components into a shared _MonolithLibrary.yaml. Handing it pro_api.json would regenerate all 164 specs from the gateway's version-filtered view of the Pro API and delete the commands that view has withdrawn. This walks one subtree, writes only the files it is given, and inlines each bucket's whole component closure so the files are self-contained — a shared library would entangle them with Split's wipe-and-regenerate contract, and PreservedSpecs' $ref scan with it.
Three transforms turn a gateway-published operation into a Pro-direct one. Each is a property of the publication rather than of the endpoint, so it is applied here rather than corrected by hand afterwards:
- Header parameters are dropped. The gateway declares X-Tenant-Id on every operation; it is the scope this CLI's own client stamps on a request (client.setScopeHeader), never a flag a user supplies, and the parser turns a declared parameter into one.
- x-required-privileges-legacy is promoted over x-required-privileges. A Pro command's jamf:privileges annotation speaks Jamf Pro API-role prose ("Read Mac Applications"); the gateway spec's x-required-privileges is the GA capability vocabulary ("applications:read"), which reaches the same command through specs/gateway/coverage.json as jamf:gateway-privileges. Publishing the slug as the Pro privilege would send an operator to a console where that grant does not exist — the same class of wrong answer the two-vocabulary split in internal/privileges exists to prevent.
- x-action is stamped on any operation whose path is deeper than its collection path and whose terminal segment is a literal. jss carries x-action and the gateway publication drops it, and the parser needs it to name an operation after its verb: without it POST .../version-update, POST .../installation-retry (both forms) and POST .../deployments all infer "create" and collide, and GET .../titles/{id}/versions collides with the canonical get. Precedent for a GET carrying it is in the repo's own specs (ComputerPrestageScopeV2, PatchSoftwareTitleConfigurations).
Returns the files written (sorted) and any warnings — an operation with no legacy privilege list is reported rather than silently published with the gateway's slug.
func Normalise ¶ added in v1.29.0
Normalise reads a consolidated OpenAPI document from source — a local path or an http(s):// URL — and writes it into specsDir as one deterministic YAML file, returning the path written.
Two transforms, and both are the reason this is not a plain copy:
- Example coercion. The document is JSON, so a `type: string` field with `example: 3` round-trips as an integer and every scaffold built from it emits a numeric literal. coerceExamples puts it back.
- Sorted, deterministic YAML. A 2 MB document written as the server sent it is one unreadable line and every ingest is an unreviewable diff. Sorting every mapping key means a spec ingest produces a diff someone can read, which is the one thing the 165-file layout was genuinely good for.
func PruneStaleSpecs ¶ added in v1.29.0
PruneStaleSpecs removes every *.yaml directly in specsDir that is not in keep, returning the names it removed. Subdirectories are untouched, and so is anything whose name begins with a dot — metadata such as .spec-version lives beside the specs.
This is what turns the old 165-file layout into the two files that replace it, and it reports every removal rather than doing it quietly: a spec file disappearing used to rename commands, and even now that it cannot, an ingest that silently deleted 163 files would be indistinguishable from one that failed halfway.
Types ¶
type SubtreeSpec ¶ added in v1.28.0
SubtreeSpec routes one branch of a path subtree into its own per-resource spec file. Prefix is the collection path the file owns; the longest matching prefix wins, so a parent and its children can both be declared.