Skip to content

Design Rationale

Why wrap the REST API

IBM MQ provides an administrative REST API, but working with it directly requires constructing JSON payloads with MQSC-specific parameter names, handling CSRF tokens, parsing nested response structures, and interpreting completion/reason code pairs.

The library wraps this complexity behind methods that map 1:1 to MQSC commands, translate attribute names to developer-friendly conventions, and surface errors as exceptions with full diagnostic context.

Single-endpoint design

All MQSC operations go through a single REST endpoint:

POST /ibmmq/rest/v2/admin/action/qmgr/{qmgr}/mqsc

The runCommandJSON payload structure carries the command verb, qualifier, object name, and parameters:

{
  "type": "runCommandJSON",
  "command": "DISPLAY",
  "qualifier": "QLOCAL",
  "name": "MY.QUEUE",
  "responseParameters": ["all"]
}

This means the library needs exactly one HTTP method, one URL pattern, and one payload schema to cover the entire MQSC command surface.

Method naming conventions

Command methods follow the pattern <verb>_<qualifier> (or <verb><Qualifier> in camelCase languages) in lowercase with spaces converted to underscores or camelCase boundaries:

MQSC command Method name pattern
DISPLAY QUEUE display_queue / displayQueue
DEFINE QLOCAL define_qlocal / defineQlocal
DELETE CHANNEL delete_channel / deleteChannel
ALTER QMGR alter_qmgr / alterQmgr

This convention provides a predictable, discoverable API without inventing new abstractions over the MQSC command set.

Return shape decisions

DISPLAY commands return a list of attribute maps. An empty list means no objects matched — this is not an error. The caller can iterate without checking for null/None.

Queue manager singletons (display queue manager status, etc.) return a single attribute map or null/None. These commands always return zero or one result, so a list would be misleading.

Non-DISPLAY commands (DEFINE, DELETE, ALTER, etc.) return nothing on success and raise an exception on failure.

Attribute mapping complexity

The mapping layer is the most complex part of the library. This complexity exists because IBM MQ uses terse uppercase tokens (CURDEPTH, DEFPSIST, CHLTYPE) that are unfriendly in application code. The mapping pipeline translates these to readable names (current_depth, default_persistence, channel_type) and back.

The translation is not a simple case conversion. The mapping tables were originally bootstrapped from IBM MQ 9.4 documentation, then customized and rationalized. They contain:

  • Key maps: Attribute name translations (e.g. CURDEPTHcurrent_depth).
  • Value maps: Enumerated value translations (e.g. "YES""yes", "SVRCONN""server_connection").
  • Key-value maps: Cases where both key and value change together.

Go-specific design choices

Zero external dependencies

mqrestadmin has zero external runtime dependencies. The entire package is built on the Go standard library:

  • net/http for HTTP communication
  • encoding/json for JSON serialization
  • crypto/tls for TLS and mutual TLS
  • embed for mapping data resources

This keeps the dependency tree empty and avoids version conflicts in downstream projects.

Single flat package

The entire public API lives in one package: mqrestadmin. There are no sub-packages to import. This follows the Go convention of small, focused packages and keeps the import path simple:

import "github.com/wphillipmoore/mq-rest-admin-go/mqrestadmin"

Functional options

Session configuration uses the functional options pattern rather than a configuration struct or builder:

session, err := mqrestadmin.NewSession(
    "https://localhost:9443/ibmmq/rest/v2",
    "QM1",
    mqrestadmin.LTPAAuth{Username: "mqadmin", Password: "mqadmin"},
    mqrestadmin.WithVerifyTLS(false),
    mqrestadmin.WithTimeout(30 * time.Second),
)

This provides a clean API with sensible defaults, optional parameters without nil checks, and backward-compatible extensibility.

context.Context integration

All I/O methods accept context.Context as their first parameter:

results, err := session.DisplayQueue(ctx, "MY.QUEUE")

This enables cancellation, deadline propagation, and tracing integration without inventing a custom mechanism.

Errors as values

Go errors follow the standard error interface with typed error structs for classification:

results, err := session.DisplayQueue(ctx, "MY.QUEUE")
if err != nil {
    var cmdErr *mqrestadmin.CommandError
    if errors.As(err, &cmdErr) {
        fmt.Printf("status: %d, payload: %v\n", cmdErr.StatusCode, cmdErr.Payload)
    }
    return err
}

There is no exception hierarchy. Errors are values that can be inspected with errors.As() and errors.Is().

Method naming conventions

Command methods use PascalCase following Go export conventions. The pattern is <Verb><Qualifier>:

MQSC command Go method
DISPLAY QUEUE DisplayQueue()
DEFINE QLOCAL DefineQlocal()
DELETE CHANNEL DeleteChannel()
ALTER QMGR AlterQmgr()

Return shapes

DISPLAY commands return ([]map[string]any, error). An empty slice means no objects matched -- this is not an error. The caller can range over the result without nil checks.

Queue manager singletons (DisplayQmgr, DisplayQmstatus, etc.) return (map[string]any, error). A nil map means no result was returned.

Non-DISPLAY commands (Define, Delete, Alter, etc.) return error only. A nil error means success.