classic

package
v1.28.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 3, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const CodegenHeader = "// Code generated by jamf-cli generator (classic). DO NOT EDIT."

CodegenHeader is the marker line at the top of every classic API generated file. Must match the first line of classicResourceTemplate and classicRegistryTemplate.

Variables

This section is empty.

Functions

func AttachSchemas added in v1.28.0

func AttachSchemas(resources []ClassicResource, art *classicschema.Artifact) error

AttachSchemas binds each Classic resource to the request-body schema the committed artifact names for it, so the template can emit --scaffold, --set and required/enum help.

A nil artifact attaches nothing and is not an error: Classic commands then ship exactly as they did before the artifact existed, reading their body from --from-file or stdin with no guidance. `make generate` has to work in a tree where nobody has fetched an SDK spec.

The schemas are loaded through kin-openapi so $ref resolves — a Classic schema refers to a dozen shared components (site, category, id_name, scope) — and then through parser.SchemaFromOpenAPI, so a Classic body is walked by the same code that walks a Pro, Platform or Security Cloud one.

Types

type ClassicFileField added in v1.11.0

type ClassicFileField struct {
	Flag                       string // CLI flag name, e.g. "mobileconfig-file"
	XMLPath                    string // Slash-delimited path under the resource root, e.g. "general/payloads"
	Encoding                   string // "xml-cdata" | "raw"
	Desc                       string // Flag description shown in --help
	NameFallback               string // "none" | "keep-ext" | "strip-ext"
	PreservePayloadIdentifiers bool   // If true, fetch existing payloads on update and call profileconvert.InjectIdentifiers
	FetchMergePut              bool   // If true, apply must fetch existing record, overlay only the file field, and PUT
}

ClassicFileField declares a resource field whose value is sourced from a local file via a dedicated CLI flag on create/update/apply. The file contents are injected as children of a named XML parent before the body is sent. Encoding controls how the file contents are embedded: "xml-cdata" wraps them in a CDATA section (for mobileconfig), "raw" inserts the XML subtree as-is (for AppConfig plists).

type ClassicResource

type ClassicResource struct {
	Name             string // e.g., "policies"
	Path             string // URL segment under /JSSResource/: "policies"
	CLIName          string // e.g., "classic-policies"
	GoName           string // e.g., "ClassicPolicies"
	Singular         string // JSON root key for a single object: "policy"
	Description      string
	Operations       []string // ["list", "get", "create", "update", "delete"]
	Lookups          []string // ["id", "name", "serialnumber", "macaddress", "udid"]
	HasScope         bool     // true if the resource supports scope operations
	IDPath           string   // path segment between base path and ID value; defaults to "id" (e.g. "groupid" → /accounts/groupid/{id})
	IsConfigProfile  bool     // true for macOS and mobile device configuration profile resources
	HasCustomPayload bool     // true only for osxconfigurationprofiles (supports --custom-payload-file)
	FileFields       []ClassicFileField
	// ListSubset marks a list operation as sharing the list endpoint with a
	// sibling resource: GET /JSSResource/{path} returns both, and the generated
	// list command extracts only the named sub-element before formatting.
	// Used for /accounts (returns users + groups combined under <accounts>).
	// Empty for normal list endpoints.
	ListSubset string
	// GroupPath is the Classic API path (without /JSSResource/) for the group
	// list/detail endpoints, e.g. "mobiledevicegroups". When set, delete gets
	// a --group flag that resolves members and deletes them in bulk.
	GroupPath string
	// Subsets is the curated list of server-side subset section names a get
	// command exposes via --subset (e.g. General, Commands for computerhistory).
	// Drives shell completion only; values are passed through to the API
	// verbatim, so unknown values still work. Empty when the resource declares
	// no subsets.
	Subsets []string
	// GatewayLevel and GatewayDetail record whether the Jamf Platform gateway
	// exposes this resource at all, from specs/gateway/coverage.json.
	// Resource-level because a Classic resource's paths are built at runtime
	// from Path plus the lookup in play, so there is no fixed set of paths to
	// enumerate. Empty when the gateway serves it or when no manifest was
	// available.
	GatewayLevel  string
	GatewayBasis  string
	GatewayDetail string
	// GatewayMethods narrows that verdict to the HTTP method a subcommand
	// sends, keyed by method. A resource can be served and still have a dead
	// subcommand: Classic API 11.28.0 withdrew every read on patchsoftwaretitles
	// while keeping POST /patchsoftwaretitles/id/{}, so the resource is carried
	// and `list`, `get`, `update` and `delete` are all refused. The method is
	// fixed at generate time even though the path is not, and a method the
	// gateway declares nowhere beneath the resource cannot work under any
	// lookup. Absent key or empty Level means served.
	GatewayMethods map[string]GatewayVerdict
	// GatewayList is the verdict for the collection GET — /JSSResource/{Path}
	// with no lookup, the one Classic path that IS fixed at generate time. It is
	// separate from GatewayMethods["GET"] because a resource can keep GET on its
	// {id} paths and lose it on the collection, which is what 11.28.0 did to
	// patchpolicies: `get` works, `list` does not.
	GatewayList GatewayVerdict
	// GatewayPrivileges are the Jamf Account capability permissions the gateway
	// requires, keyed by HTTP method — per method rather than per resource
	// because that is the granularity that differs (accounts:read for a GET,
	// accounts:update for a PUT), even though the served/unserved verdict above
	// is resource-wide. A different vocabulary from Jamf Pro's own privilege
	// names, and Classic commands carry none of those.
	GatewayPrivileges map[string][]string
	// BodySchema is the request-body shape for create/update/apply, parsed from
	// the committed specs/classic/schemas.json artifact. Nil when the artifact
	// is absent or names no schema for this resource — six of the manifest's
	// resources have none, four of them withdrawn from the Classic API
	// altogether — in which case the resource ships without --scaffold, --set or
	// field help, exactly as every Classic resource did before the artifact
	// existed.
	//
	// The Classic manifest is hand-written and carries no field information, so
	// this is the only route by which a Classic write command can say what goes
	// in its body.
	BodySchema *parser.Schema
	// BodyRoot is the XML root element a request body must be wrapped in, e.g.
	// "policy". Read off the spec (a schema's xml.name, else its component key)
	// rather than reused from Singular, so a disagreement between the two is
	// reported at derivation time instead of silently picking one.
	BodyRoot string
	// BodySchemaName is the component schema key BodySchema was parsed from,
	// recorded so generated help can name its provenance.
	BodySchemaName string
}

ClassicResource represents a Classic API resource parsed from the YAML manifest.

func ParseManifest

func ParseManifest(path string) ([]ClassicResource, error)

ParseManifest reads the Classic API YAML manifest and returns a sorted slice of ClassicResource structs with all defaults applied.

func (ClassicResource) CredentialFields added in v1.28.0

func (r ClassicResource) CredentialFields() []string

CredentialFields lists the dotted paths whose value is a credential, so the generated --set refuses them.

The repo's credential policy forbids passwords, tokens and client secrets in flag values, because a flag value lands in shell history, in `ps` output and in CI logs. None of the three existing --set implementations enforces it, and on the Classic surface that gap is wide: an SMTP server, an LDAP server, a distribution point, a GSX connection, a VPP account and a directory binding all carry one, and `apply`/`create`/`update` are exactly the commands a caller reaches for. So Classic refuses the pair and names --from-file instead, rather than inheriting a hazard the policy already rules out.

Matched on the field name — or, where the leaf name alone is too generic to be safe, on the dotted path — and not on a schema marker: the Classic spec declares writeOnly 28 times in total and on none of these fields.

func (ClassicResource) EnumChoices added in v1.28.0

func (r ClassicResource) EnumChoices() []EnumChoice

EnumChoices lists every enum-constrained field in the resource's body schema, as dotted paths with a "[]" segment marking an array element.

This is the part of the schema the wire will not teach you. Probed on a live tenant 2026-09-02: the Classic API does not enforce its own enums. A policy created with `<frequency>Twice per fortnight</frequency>` answers 201 and reads back `Once per computer`; a computer group criterion with `<and_or>maybe</and_or>` answers 201 and reads back `and`. The value is silently replaced with the default, with no error and no warning — so a caller who guesses wrong gets a working object that does the wrong thing, and --help is the only place the legal set can come from.

func (*ClassicResource) ExtraLookups

func (r *ClassicResource) ExtraLookups() []string

ExtraLookups returns lookups beyond "id" (e.g., name, serialnumber, macaddress, udid).

func (ClassicResource) HasBodySchema added in v1.28.0

func (r ClassicResource) HasBodySchema() bool

HasBodySchema reports whether a resource carries enough shape for --scaffold and --set to be worth emitting.

func (*ClassicResource) HasLookup

func (r *ClassicResource) HasLookup(lookup string) bool

HasLookup returns true if the resource supports the given lookup type.

func (*ClassicResource) HasOperation

func (r *ClassicResource) HasOperation(op string) bool

HasOperation returns true if the resource supports the given operation.

func (ClassicResource) RepeatedElementKeys added in v1.28.0

func (r ClassicResource) RepeatedElementKeys() []string

RepeatedElementKeys returns RepeatedElements' keys in sorted order.

func (ClassicResource) RepeatedElements added in v1.28.0

func (r ClassicResource) RepeatedElements() map[string]string

RepeatedElements maps an array-typed dotted path to the element name its members are wrapped in, so the runtime --set builder can render XML.

Classic models a repeated element as a JSON array whose items carry one named object child: `<criteria><criterion>…</criterion></criteria>` is declared as `criteria: {type: array, items: {properties: {criterion: {...}, size: {...}}}}`. The element name is not derivable from the array's own name — `criteria` holds `criterion`, `computers` holds `computer`, `scope.limit_to_users.user_groups` holds `user_group` — so it is read off the schema and recorded here.

func (ClassicResource) RequiredFields added in v1.28.0

func (r ClassicResource) RequiredFields() []string

RequiredFields lists the fields the spec marks required on the body's own schema, in sorted order.

Worth surfacing because the server's answer to a missing required field is an HTTP 409 carrying an HTML error page — `Error: ComputerGroup name is required` buried in a `<body style="font-family: sans-serif;">` — one field at a time. It is discoverable, but only by trying.

Top level only, and deliberately not the whole tree. A nested `required` means "required *if* the enclosing object is sent", and every Classic object that declares one sits under an optional parent: computer_group's only nested entry is site.name, so reporting it would tell a caller that a group cannot be created without naming a site, which is false. When a Classic schema first declares a required object, this needs to grow the ancestor check rather than the whole tree.

func (ClassicResource) ScaffoldXML added in v1.28.0

func (r ClassicResource) ScaffoldXML() (string, error)

ScaffoldXML renders the resource's request-body template as XML.

func (ClassicResource) SetCompletions added in v1.28.0

func (r ClassicResource) SetCompletions() []string

SetCompletions lists the scalar dotted paths worth offering as shell completions for --set, each with a trailing "=".

Two exclusions, both because a completion is a suggestion and suggesting something that will be refused is worse than suggesting nothing:

  • Objects and arrays. --set cannot set either as a whole, and the generated parser refuses them by name with an explanation.
  • Credential fields. --set refuses these on purpose, so offering `read_write_password=` at the shell prompt would walk a caller straight into the thing the credential policy exists to prevent — and it is the shell that records the command line.

func (ClassicResource) SetFieldTypeKeys added in v1.28.0

func (r ClassicResource) SetFieldTypeKeys() []string

SetFieldTypeKeys returns SetFieldTypes' keys in sorted order, so the generated literal is stable across runs.

func (ClassicResource) SetFieldTypes added in v1.28.0

func (r ClassicResource) SetFieldTypes() map[string]string

SetFieldTypes maps every settable dotted path in the body schema to its spec type, for the generated --set parser to coerce against.

Schema-driven rather than a looks-like-JSON heuristic, following the modern Pro generator: docs/solutions/logic-errors/set-array-object-stringification-2026-07-24.md records what the heuristic costs, and Classic makes it worse. Classic XML is untyped on the wire, so `--set general.name=42` has to stay the string "42" and a boolean field has to render `true` rather than `1`; nothing in the response would reveal a wrong guess.

func (ClassicResource) TopLevelOptionalFields added in v1.28.0

func (r ClassicResource) TopLevelOptionalFields() []string

TopLevelOptionalFields lists the resource's top-level body sections that are not required, in sorted order.

Top level only, deliberately. A Classic policy has 271 fields at depth 5, and an exhaustive optional list in --help would bury the required one; the full tree is what --scaffold is for.

type EnumChoice added in v1.28.0

type EnumChoice struct {
	Path   string
	Values []string
}

EnumChoice is one constrained field and the values it accepts.

type GatewayVerdict added in v1.28.0

type GatewayVerdict struct {
	Level  string
	Basis  string
	Detail string
}

GatewayVerdict is one gateway-coverage verdict in the three string values the template renders as annotations. Strings rather than generator/gateway's own types so this package needs no dependency on it — generator/main.go converts at the one point the two meet.

type Generator

type Generator struct {
	// contains filtered or unexported fields
}

Generator generates Go command files from ClassicResource definitions.

func NewGenerator

func NewGenerator(outputDir string) *Generator

NewGenerator creates a new Classic API code generator.

func (*Generator) Generate

func (g *Generator) Generate(resource ClassicResource) (string, error)

Generate writes a Go command file for a single Classic API resource.

func (*Generator) GenerateRegistry

func (g *Generator) GenerateRegistry(resources []ClassicResource) (string, error)

GenerateRegistry writes the classic_registry.go file that registers all Classic commands.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL