Documentation
¶
Index ¶
- Constants
- func ApplyCreateOpOverrides(resources []*Resource)
- func ApplyFileFields(resources []*Resource)
- func ApplyGetDetailPaths(resources []*Resource)
- func ApplyListDetailPaths(resources []*Resource)
- func ApplyLookupFields(resources []*Resource)
- func ApplyNameFieldOverrides(resources []*Resource)
- func ApplyNameOverrides(resources []*Resource)
- func ApplyTableColumns(resources []*Resource)
- func ApplyUpdateTokenOpOverrides(resources []*Resource)
- func ClassicIsCountElement(name string, prop *Property, siblings *Schema) bool
- func ClassicRepeatedElement(items *Schema) string
- func HasScaffoldShape(s *Schema) bool
- func ScaffoldJSON(s *Schema) (string, error)
- func ScaffoldXML(s *Schema, root string) (string, error)
- func SecurityScopeForFile(specPath string) string
- type FileField
- type Generator
- type LookupField
- type OpPathMethod
- type Operation
- type Parameter
- type Property
- type RequestBody
- type Resource
- type Response
- type ScalarField
- type Schema
- type StatusResult
- type TableColumn
Constants ¶
const ClassicCountElement = "size"
ClassicCountElement is the name of the count element the Jamf Pro Classic API puts inside every repeated-element wrapper: `<criteria><size>1</size>…`.
It is server-computed and the spec does not mark it readOnly — it appears 104 times, 98 of them as a plain `$ref` to a shared integer schema — so nothing but ClassicIsCountElement keeps it out of request templates. Wire-checked 2026-09-02: a create carrying `<size>` and one omitting it both answer 201, so dropping it is safe as well as correct.
const CodegenHeader = "// Code generated by jamf-cli generator. DO NOT EDIT."
CodegenHeader is the marker line at the top of every modern API generated file. Must match the first line of resourceTemplate and registryTemplate.
Variables ¶
This section is empty.
Functions ¶
func ApplyCreateOpOverrides ¶ added in v1.12.0
func ApplyCreateOpOverrides(resources []*Resource)
ApplyCreateOpOverrides renames operations listed in resourceCreateOpOverrides to "create" so the generator emits them as the resource's canonical create command. Must be called after DeduplicateVersioned/ApplyNameOverrides so resource names are canonical.
func ApplyFileFields ¶ added in v1.11.0
func ApplyFileFields(resources []*Resource)
ApplyFileFields sets FileFields on resources listed in resourceFileFields. Must be called after DeduplicateVersioned/ApplyNameOverrides so names are canonical.
func ApplyGetDetailPaths ¶
func ApplyGetDetailPaths(resources []*Resource)
ApplyGetDetailPaths configures "get" commands to use a richer detail endpoint. For resources whose get has a section parameter (e.g. computers-inventory), the detail path is stored in GetDetailPath and used as the default — specifying --section overrides back to the original path. For resources without section filtering (e.g. mobile-devices), the get path is swapped outright. Must be called after ApplyNameOverrides.
func ApplyListDetailPaths ¶
func ApplyListDetailPaths(resources []*Resource)
ApplyListDetailPaths swaps the "list" operation's path to a richer detail endpoint and injects a section query parameter. Must be called after ApplyNameOverrides and before ApplyTableColumns.
func ApplyLookupFields ¶
func ApplyLookupFields(resources []*Resource)
ApplyLookupFields sets LookupFields and GroupsClassicPath on resources. Must be called after DeduplicateVersioned so resource names are canonical.
func ApplyNameFieldOverrides ¶
func ApplyNameFieldOverrides(resources []*Resource)
ApplyNameFieldOverrides corrects NameField and IDField values that the auto-detection heuristics got wrong. Must be called after ApplyNameOverrides so resource names are in their final canonical form.
func ApplyNameOverrides ¶
func ApplyNameOverrides(resources []*Resource)
ApplyNameOverrides corrects resource names that auto-pluralization got wrong. Must be called after DeduplicateVersioned.
func ApplyTableColumns ¶
func ApplyTableColumns(resources []*Resource)
ApplyTableColumns sets TableColumns and DefaultSections on resources that have preferred column configuration. Must be called after ApplyNameOverrides.
func ApplyUpdateTokenOpOverrides ¶ added in v1.12.0
func ApplyUpdateTokenOpOverrides(resources []*Resource)
ApplyUpdateTokenOpOverrides detaches the configured auxiliary token-update op from the resource's Operations slice and records it on r.UpdateTokenOp. The main update command then composes a token PUT + a body PUT based on which flags are supplied.
func ClassicIsCountElement ¶ added in v1.28.0
ClassicIsCountElement reports whether a property named `size` is the Classic API's collection counter rather than a field in its own right.
The name is overloaded in the spec and a blanket drop would delete real data. Of the 104 occurrences, two are physical capacities in MB, not counts: computer_post's `hardware.storage[].device.size` (example 512287) and the `partition.size` beneath it. Neither is reachable today — no Classic resource in specs/classic/resources.yaml binds computer_post, since Pro's modern computers-inventory covers computers — and TestNoBoundResourceCarriesASemanticSizeField fails if an ingest ever binds one.
The discriminator is a repeated sibling: a counter counts something, so it only ever appears beside an array or, in an array-item wrapper, beside the object being repeated. `partition`, whose twelve siblings are all scalars, is correctly kept by that test. `device` is not — it has a `partition` array beside it — which is exactly why the guard test exists rather than a cleverer rule: the honest fix when that case becomes reachable is to read the spec again, not to guess harder now.
func ClassicRepeatedElement ¶ added in v1.28.0
ClassicRepeatedElement returns the element name a Classic array's members are wrapped in, or "" when the array does not have that shape.
The Classic API is XML, and its JSON representation renders a repeated element as an array of single-key objects:
<criteria> criteria: {type: array, items: {properties: {
<size>1</size> criterion: {…},
<criterion>…</criterion> size: {$ref: size}
</criteria> }}}
So the element name lives one level below the array, as the name of the items schema's sole object-valued property. It is not derivable from the array's own name: `criteria` holds `criterion`, `computers` holds `computer`, and `scope.limit_to_users.user_groups` holds `user_group`.
Returns "" for an array of scalars and for an items schema with more than one object-valued property, neither of which has an unambiguous element name.
func HasScaffoldShape ¶ added in v1.28.0
HasScaffoldShape reports whether a request-body schema carries enough shape for --scaffold to be worth offering: named properties, or an array whose element has them.
The array arm is not hypothetical padding. The Jamf Pro generator gated on "has properties" alone, and a bare-array request body has none — so `pro app-requests update`, the one Pro endpoint whose body is a top-level array, shipped with no --scaffold flag at all rather than with an unhelpful one. A schema with neither still gets no flag, because a scaffold of "{}" tells a caller nothing.
func ScaffoldJSON ¶ added in v1.28.0
ScaffoldJSON returns a pretty-printed JSON template for a request body, derived from its parsed schema. It backs every generator's --scaffold output: the modern Jamf Pro generator, the Platform Gateway generator and the Jamf Security Cloud generator all call this, so a scaffold means the same thing whichever API a command belongs to.
Before this was shared, the three had drifted into three different answers for the same schema. Pro skipped read-only fields and honoured spec examples but never descended into nested objects; platform and security descended into objects but ignored both examples and read-only. All three rendered an array as "[]" regardless of what it held, which is the gap this exists to close — `platform-device-groups create --scaffold` emitted "criteria": [] for a five-field element, and the only feedback on a wrong guess was a 400.
The rules, and why:
- Read-only properties are omitted. This is a request template and the server rejects or ignores them.
- Write-only properties are kept. They are the ones a caller most needs prompting for — passwords, secrets, keystores — and they never appear in a GET, so a scaffold is the only place they surface. See docs/solutions/logic-errors/write-only-fields-dropped-by-update-set-2026-07-22.md.
- A spec example wins over a synthesised placeholder. A real value teaches the format; "" does not.
- An array shows one element when the element is an object, and stays empty otherwise. The object shape is the information a caller cannot guess; a scalar element's type is evident from the field and its help text, and rendering [""] would imply an empty string is a meaningful entry.
Returns "{}" for a nil schema, so a template can embed the output unconditionally. A marshalling failure is returned, not swallowed: this runs at generation time, and the alternative is an operation that ships a scaffold of "{}" while `make generate` exits 0 — the silent-shortening failure mode that TestParseSchema_DepthCapUnreachedByLiveSpecs exists to prevent one tier up.
func ScaffoldXML ¶ added in v1.28.0
ScaffoldXML returns an indented XML template for a Classic API request body, derived from its parsed schema and wrapped in the given root element.
It is the XML sibling of ScaffoldJSON and shares its rules — read-only properties omitted, write-only kept, a spec example preferred over a placeholder, one element shown for an array of objects — so a scaffold means the same thing whichever API a command belongs to (docs/solutions/conventions/one-scaffold-walker-2026-08-20.md).
It is a separate renderer rather than a re-marshal of ScaffoldJSON's output because the JSON is not the wire format here: the Classic API takes XML, and three of its shapes have no faithful JSON round trip. Repeated elements collapse a wrapper level (see ClassicRepeatedElement), the `size` count element has to be dropped, and element order carries meaning in XML while a JSON object's key order does not — so the template is built straight from the schema.
Two extra rules of its own, both wire-checked against a live tenant on 2026-09-02:
- The `size` count element is dropped. It is server-computed, and a create carrying it and one omitting it both answer 201.
- Every `id` the spec declares is kept, including a resource's own. A body `id` is inert on the Classic API: a create sending `<id>99999</id>` answered 201 and assigned 226, and a PUT to /networksegments/id/228 carrying `<id>229</id>` updated 228 while leaving 229 untouched — the URL wins. So there is nothing to protect a caller from, and the alternative is worse: most `id` elements in a Classic body are foreign keys the caller is supposed to supply (a policy's category, site, dock item and directory binding all reference one), so a rule that stripped them would have to distinguish identity from reference, and getting that wrong silently removes the field that binds a policy to its category.
func SecurityScopeForFile ¶ added in v1.23.0
SecurityScopeForFile returns the internal/security.Client scope name ("Risk", "Lifecycle", or "SSE") that owns every resource parsed out of the given spec filename — used by generator/security to pick which DoExpect{Scope} method generated commands call. Empty when the file isn't a recognized Security Cloud spec.
Types ¶
type FileField ¶ added in v1.11.0
type FileField struct {
Flag string // CLI flag name, e.g. "script-file"
Field string // Request-body property that receives the file contents, e.g. "scriptContents"
Encoding string // "raw" (string) or "base64"
Desc string // Flag description shown in --help
CompanionField string // Optional: body property auto-populated with filepath.Base(path) when absent (e.g. "tokenFileName" for DEP)
NameFallback string // "none" | "keep-ext" | "strip-ext" — when the body lacks a name, derive one from the filename
NameFlag bool // When true, emit a --name flag on create/apply/upload-style ops that sets the body's name field (for tokens whose filename makes a poor record name).
RenameAfterUpload bool // When true, and --name is supplied on an upload-style op whose request schema rejects a name field (e.g. DEP /upload-token → DeviceEnrollmentTokenDto has only encodedToken + tokenFileName), the generator emits a follow-up GET+PUT on the standard update path to apply the name.
}
FileField declares a resource field whose value is sourced from a local file via a dedicated CLI flag on create/update/apply/patch. The file contents are injected into the request body pre-marshal, overwriting any value the caller may have supplied in the body. Encoding, companion-field population, and name fallback are all driven per entry.
type Generator ¶
type Generator struct {
// contains filtered or unexported fields
}
Generator generates Go code from parsed resources
func NewGenerator ¶
NewGenerator creates a new code generator
type LookupField ¶
type LookupField struct {
Flag string // CLI flag name (e.g. "serial")
RSQLField string // RSQL filter field path (e.g. "hardware.serialNumber")
Desc string // Flag description shown in --help
Section string // Optional inventory section to request so the RSQLField is present in the response (e.g. "HARDWARE"); empty when the field is in the default section.
}
LookupField represents an alternate identifier that can be used to resolve a resource ID instead of the primary name field (e.g. serial number for computers).
type OpPathMethod ¶ added in v1.12.0
OpPathMethod is a (path, method) pair used to identify an operation for overrides.
type Operation ¶
type Operation struct {
Name string // e.g., "list", "get", "create"
Method string // HTTP method
Path string // API path
Summary string
Description string
Parameters []*Parameter
RequestBody *RequestBody
Responses map[string]*Response
IsAction bool // x-action: true
IsDestructive bool // Requires confirmation (delete, erase, etc.)
IsList bool // List operation with pagination support
IsPaginated bool // Any GET with pagination params (broader than IsList); gates --all/--limit auto-pagination
APIVersion string // v1, v2, preview, etc.
Privileges []string // x-required-privileges
// FallbackPaths holds lower-version base paths for GET/DELETE ops where the
// same endpoint exists at multiple API versions. Listed in descending version
// order so the runtime tries the newest fallback first.
FallbackPaths []string
// BulkActionPath is set on a per-{id} x-action when the spec also declares a
// sibling collection-level action of the same name (e.g. the per-deployment
// installation-retry and the no-{id} bulk installation-retry). It holds the
// bulk endpoint's path; the generator surfaces it as an --all flag that hits
// the collection-level endpoint in a single call instead of the {id} one.
BulkActionPath string
// StatusResults lists non-2xx statuses this operation documents as results
// rather than failures (see documentedStatusResults in parser.go). The
// generated command carries them through registry.WithAllowedStatuses and
// renders their body instead of letting the client map them to an
// exit-code error. Empty for all but a handful of check-style endpoints.
StatusResults []StatusResult
// NoContentDescription is the 204 response's description, set only when
// StatusResults is non-empty — a 204 has no body, so the generated command
// synthesizes one from this so the success case is machine-readable too.
NoContentDescription string
// ExpectedStatus is the success status the server actually answers, from
// the published spec's x-jamf-expected-status extension. Non-zero only
// where the SDK found the declared status wrong by probing the wire; it
// overrides the status derived from the responses map.
ExpectedStatus int
// GatewayLevel, GatewayBasis and GatewayDetail record whether the Jamf
// Platform gateway exposes this operation, from specs/gateway/coverage.json.
// Empty when the gateway serves it or when no manifest was available. Basis
// is the evidence ("probe" or "unpublished") and selects the wording of the
// refusal, not whether there is one. See generator/gateway.
GatewayLevel string
GatewayBasis string
GatewayDetail string
// GatewayPrivileges are the Jamf Account capability permissions the gateway
// requires for this operation, also from specs/gateway/coverage.json. A
// different vocabulary from Privileges above, not a translation of it: that
// field holds the Jamf Pro API-role privilege names an instance enforces,
// and the GA consolidation folded several of those into one capability. Both
// are surfaced in the commands catalog so an integration can be sized
// without provoking a 403.
//
// Empty for an unserved operation — the published spec declares no scope for
// what it does not publish — and for the 44 unauthenticated Jamf Pro
// endpoints.
GatewayPrivileges []string
}
Operation represents an API operation (endpoint)
type Parameter ¶
type Parameter struct {
Name string
In string // "query", "path"
Description string
Required bool
Type string
Default any
IsArray bool
}
Parameter represents a query/path parameter
type Property ¶
type Property struct {
Name string
Type string
Description string
Example any
Nullable bool
ReadOnly bool
WriteOnly bool // true when the field is accepted in requests but never returned in responses (e.g. passwords, secrets)
SchemaRef string // name of the referenced component schema for object/array types (e.g. "ComputerGeneralUpdate")
Nested *Schema // resolved nested schema for object types (may be nil)
// VariantOnly marks a property that only a non-scaffolded variant of a
// discriminated-union body declares. It carries enum values for the help and
// nothing else — no type, no example — so a scaffold must not render it: it
// is not a field of the body the scaffold shows.
VariantOnly bool
// Enum holds the values this property is restricted to, in the order the
// spec lists them, rendered as literals. Empty for unconstrained
// properties. Not string-only: an integer enum is carried the same way,
// because a required field constrained to five specific durations is
// exactly the case help has to name.
//
// Carried so generated help can name the choices. A scaffold renders an
// enum field as an empty string like any other, which tells a caller
// nothing about what it accepts — and Security Cloud's ZTNA gateway vendor
// is a case-sensitive eleven-value enum whose rejection is a 400 that does
// not name the offending field, so guessing is expensive.
Enum []string
// Items is the element schema for an array-typed property, so a scaffold can
// show one element instead of a bare "[]". Nil when the element is a scalar
// or the spec declares no items.
//
// This is the array counterpart of Nested, and it is populated under a
// recursion cap that Nested never needed: an object property whose own
// properties are empty ends the walk, but an array property can name its
// parent's schema as its element type (a tree with a children[] of itself),
// which would recurse forever.
Items *Schema
}
Property represents a schema property
type RequestBody ¶
type RequestBody struct {
Description string
Required bool
Schema *Schema
IsMultipart bool // true when content type is multipart/form-data
IsMergePatch bool // true when content type is application/merge-patch+json
FileField string // schema property that holds the binary file (e.g. "file")
}
RequestBody represents a request body
type Resource ¶
type Resource struct {
Name string // e.g., "buildings"
NameSingular string // e.g., "building"
GoName string // e.g., "Buildings"
Description string
Operations []*Operation
Schemas map[string]*Schema
NameField string // Filter field for name lookups (default "name", some use "displayName")
IDField string // Response field for ID extraction in name resolution (default "id", some use "templateId", "groupId", etc.)
IsSingleton bool // True for settings-style resources: single object, GET+PUT, no {id} in any path
LookupFields []LookupField // Alternate identifier fields for patch-by-name / delete-by-name (e.g. serial number)
NameLookupPath string // Override list path for name resolution (when the standard list endpoint ignores RSQL)
NameLookupIDField string // Override ID field extracted from NameLookupPath response (when it differs from IDField)
HasVersionLock bool // True when PUT/POST request body includes versionLock (optimistic locking for prestages)
GroupsClassicPath string // When set, delete gets --group resolved via Classic API group list (e.g. "computergroups")
FileFields []FileField // File-sourced request-body fields (attached via --script-file, --token-file, etc.)
TableColumns []TableColumn // Preferred columns for list table output (when set, overrides generic column selection)
DefaultSections []string // Default --section values for list (when set, fetches these sections for table output)
GetDetailPath string // When set, "get" uses this path by default (returns all sections). If the get op has a section param, --section overrides back to the original path.
UpdateTokenOp *Operation // Optional: auxiliary PUT endpoint for file-field payloads (e.g. PUT /{id}/upload-token). When set, update/apply route the file-field flag to this endpoint instead of the main update body, and no standalone subcommand is emitted for it.
}
Resource represents a parsed API resource (e.g., buildings, computers)
func DeduplicateVersioned ¶
DeduplicateVersioned consolidates multi-version resources so each resource group surfaces as a single command using the latest API version.
When multiple spec files cover the same resource at different API versions (e.g. MobileDevicePrestagesV2.yaml + MobileDevicePrestagesV3.yaml), the generator produces commands like "mobile-device-prestages-v-2s" and "mobile-device-prestages-v-3s". This function:
- Detects versioned resource names via the "-v-{N}s" suffix pattern
- For each version family, keeps only the highest version
- Renames the winning resource to the clean canonical name (no version suffix)
- Suppresses any non-versioned base resource that the versioned family supersedes
A resource whose *name* carries no version suffix is not therefore the older one, and assuming it was cost the CLI its newest computer inventory endpoint. ComputersInventory.yaml declares both /v1 and /v4 computers-inventory, so the within-file deduplication leaves one resource named "computers-inventories" serving v4 — while ComputersInventoryV2.yaml and ComputersInventoryV3.yaml name theirs "computers-inventory-v-2s" and "-v-3s". The suffix rule read the v4 resource as the legacy base and suppressed it, so every `pro computers-inventory` command sent /v3 and the two v4-only operations (erase, remove-mdm-profile) never became commands at all. Nothing failed: v3 answers, and the gateway published all four versions.
The gateway's published spec is what exposed it. Its 11.31.0 drop now carries v4 alone — v1, v2 and v3 are withdrawn — so the version this CLI happened to send became the one refused before a request is sent, and the version it had all along became the only one served. So the family is ranked by the API version each resource actually serves, read off its operation paths, with the name suffix only telling us which resources are in the family.
func ParsePlatformSpec ¶ added in v1.15.0
ParsePlatformSpec parses a Platform Gateway OpenAPI spec and returns one Resource per operation tag. Platform paths share an /api/{service}/{version}/ prefix that the runtime fills from auth context — it is not a per-call parameter. This loader strips the prefix to /v1/ and removes the tenantId path parameter from each operation before parsing.
Resources are grouped by the first tag on each operation. Operations without tags fall back to filename-based grouping via ParseSpec.
func ParseSecuritySpec ¶ added in v1.23.0
ParseSecuritySpec parses one Jamf Security Cloud OpenAPI spec (Risk, Device Lifecycle, or Shared Signals & Events) into Resources, using securityOpsByFile's hand-authored operation list. Strips the manually declared "authorization" header parameter every operation carries (the runtime injects the scoped bearer token itself, via internal/security.Client) and, for Device Lifecycle, the {customerId} path parameter (backfilled at request time from the login JWT, the same way the Platform parser backfills {tenantId}).
func ParseSpec ¶
ParseSpec parses an OpenAPI spec file and returns one or more Resources. Most specs produce a single resource, but specs with multiple sibling collection paths (e.g. /v1/foo/macos and /v1/foo/ios in the same file) produce one resource per family. Returns nil when the file should be skipped.
type Response ¶
type Response struct {
StatusCode string
Description string
Schema *Schema
IsBinary bool // true for image/* content types, text/csv, or format:binary schemas
}
Response represents an API response
type ScalarField ¶
type ScalarField struct {
Path string // dot-notation path, e.g. "general.managed"
Type string // scalar type: "string", "integer", "boolean", "number"
}
ScalarField represents a patchable scalar field in dot-notation (e.g. "general.managed").
type Schema ¶
type Schema struct {
Name string
Type string
Properties map[string]*Property
Required []string
// Enum holds the values this schema is restricted to, when the schema is
// itself a constrained scalar rather than an object. For an array property
// that is where the constraint lives — the enum sits on the element schema,
// not on the array — so Items.Enum is how an "array of one of these" is
// discovered.
Enum []string
// Items is the element schema when Type is "array", for a schema that is
// itself an array rather than an object. Set only for arrays, and only as
// deep as parseSchema's recursion cap allows.
//
// Needed because a request body may be a bare array — the DNS whole-list
// replaces are — and without this such a body has no properties and no
// element shape, so a scaffold for it can only be "[]".
Items *Schema
// Variants names the alternative shapes of a discriminated union request
// body (a bare oneOf/anyOf), in spec order, and Discriminator the property
// that selects between them. Both empty for an ordinary schema.
//
// The schema itself carries the FIRST variant's properties, so every
// consumer — the scaffold, --set completion, the enum help — keeps working
// on a concrete shape rather than having to understand unions. Variants
// exists so the generated help can say that other shapes are legal, which is
// the part a caller cannot otherwise discover: uem-connectors create is one
// of these, and before this the whole body parsed to nothing, taking
// --scaffold and every "Allowed values:" line with it.
Variants []string
// Discriminator is the property whose value selects the variant.
Discriminator string
}
Schema represents a JSON schema
func ClassicArrayElementSchema ¶ added in v1.28.0
ClassicArrayElementSchema returns the schema of one member of a Classic array: the sole object-valued property's own schema when the array has the repeated-element shape, and the items schema itself otherwise.
This is what makes a dotted path skip the wrapper. A criterion's fields are addressed as `criteria[].name`, not `criteria[].criterion.name`, because the wrapper is an artefact of rendering XML as JSON rather than a level a caller should have to type.
func SchemaFromOpenAPI ¶ added in v1.28.0
SchemaFromOpenAPI parses one resolved OpenAPI schema into the generator's own Schema tree. It is the exported door onto parseSchema, opened for the Classic API generator: Classic resources are declared by a YAML manifest rather than by a spec, so generator/classic has schemas to parse but no operation to parse them from.
Exported rather than reimplemented because the schema walk is where the interesting decisions live — allOf composition, discriminated unions, the array-element recursion cap, enum value rendering — and a second copy of it would drift the way the three --scaffold builders did before docs/solutions/conventions/one-scaffold-walker-2026-08-20.md.
type StatusResult ¶ added in v1.27.0
StatusResult is a non-2xx response the API documents as a meaningful outcome of the operation rather than a failure of it.
type TableColumn ¶
type TableColumn struct {
Field string // JSON field path (e.g., "general.name") — may use dot-notation for nested fields
Label string // Display label (e.g., "name") — used as the column header
}
TableColumn defines a preferred column for list table output.