Skip to content
QRun-IOPublic

About

Reference CLI for the QQQ UI API (/qqq/v1), with the generated TypeScript client, a mock server and a conformance suite for client authors.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

qqq-cli

The reference command-line client for QQQ applications, and the kit for writing your own CLI, TUI or native client. Everything here talks to a QQQ server only through the UI API (/qqq/v1), the API the web UI uses too.

Package What it is
@qrun-io/qqq-cli qqq, the reference CLI: device-code sign-in, apps, tables, filtered queries, records, processes, jobs, and a live event stream
@qrun-io/qqq-ui-api-client A typed TypeScript client, generated from the merged UI API document (core plus extensions). It also does OAuth2 device-flow sign-in, token refresh, and reads the realtime (SSE) stream
@qrun-io/qqq-ui-api-conformance A black-box conformance suite you can run against any server, plus qqq-dev-idp, a local OIDC provider for testing native clients

The packages are published to GitHub Packages:

echo "@qrun-io:registry=https://npm.pkg.github.com" >> ~/.npmrc
npm install -g @qrun-io/qqq-cli@rc

Requires Node.js 20 or later and a QQQ server with UI API 1.6.0 or later (QQQ 4.1.0-RC.15 or later).

The CLI

$ qqq login --server https://admin.example.com
To sign in to https://admin.example.com, open:

  https://auth.example.com/device?code=ED84-CC03

and confirm the code ED84-CC03 (it expires in 10 minutes).
Waiting for approval...
Signed in to https://admin.example.com as darin@example.com.

$ qqq tables --app peopleApp
name     label     insert  edit  delete
-------  --------  ------  ----  ------
person   Person    yes     yes   yes
pet      Pet       yes     yes   yes

$ qqq query person --where "firstName ~ a" --where "id < 100" --order -id --limit 5
Id  First Name  Last Name  Annual Salary  ...
5   Morgan      Sample     $1,500,000.00
3   Casey       Sample     $300,000.00
(2 records)

$ qqq get person 1
$ qqq run greetInteractive --records 1,2 --value greetingPrefix=Hi
$ qqq jobs --all
$ qqq tail --channel table:person
Command UI API operations
login getAuthenticationMetaData, then the provider's device-authorization grant with values.nativeClientId
whoami getVersion, getMetaData
apps, tables [--app] getMetaData
query <table> [--where "field OP value"]... [--or] [--order [-]field] [--limit n] [--fields a,b] getTableMetaData, queryTable
get <table> <pk> getTableMetaData, getRecord
run <process> [--value k=v]... [--records ids] getProcessMetaData, initProcess, stepProcess, getProcessStatus
jobs [--all] listJobs
tail [--channel ...] openRealtimeStream, updateRealtimeSubscriptions (user:me, plus your channels)

Add --json for machine-readable output. Errors print the server's message, code and traceId.

Where things are kept:

  • Tokens go in ~/.config/qqq-cli/credentials.json (mode 0600), one entry per server. The CLI refreshes the access token at the provider before it expires, and once more on a 401.
  • QQQ_TOKEN overrides the stored token with any bearer token, such as a QQQ access token exchanged for a JWT, or a session UUID.
  • QQQ_SERVER sets the default server.

Every request carries X-QQQ-Client: cli/qqq-cli <version>, so the server's audits attribute it to the CLI.

Writing a client

import { createUiApiClient, unwrap, getNativeSignInConfig, deviceSignIn } from '@qrun-io/qqq-ui-api-client'

const config = await getNativeSignInConfig('https://admin.example.com')
const tokens = await deviceSignIn(config, (p) => console.log(`Open ${p.verificationUriComplete}`))
const api = createUiApiClient({ baseUrl: 'https://admin.example.com', token: () => tokens.accessToken, clientChannel: 'tui/my-tui 1.0' })

const page = await unwrap(api.POST('/qqq/v1/table/{tableName}/query', {
  params: { path: { tableName: 'person' } },
  body: { filter: { criteria: [{ fieldName: 'lastName', operator: 'STARTS_WITH', values: ['K'] }], limit: 20 } },
}))

Paths, parameters, bodies and responses are typed from the document. Read the QQQ docs before you write a client:

Mock server

npm run mock serves the merged document with Prism on http://127.0.0.1:4010, so you can build a client without a server. Responses come from the document's examples and schemas.

Conformance suite

qqq-ui-api-conformance --server https://admin.example.com              # public checks only
qqq-ui-api-conformance --server https://admin.example.com --token "$T"  # all read-only checks

The suite only reads. It never writes. It checks that:

  • the version and the served document agree;
  • every operation has a unique operationId and every $ref resolves;
  • every extension the server lists has operations;
  • requests without credentials or with forged tokens (alg: none, garbage) get a 401 with the error schema;
  • metadata, table metadata (with clientCapabilities), query, count, record, jobs and realtime responses validate against the document;
  • a 404 has the error schema, and X-Request-Id comes back as traceId.

Testing a client locally

qqq-dev-idp is a small OIDC provider for local development only: device grant, refresh and RS256 JWTs, with one issuer per client, as Authentik does it. Run it with the QQQ sample app:

npx qqq-dev-idp --port 9400 --client sample-app --client sample-cli --user you@example.com --groups admins --auto-approve

# in a qqq checkout (QQQ 4.1.0-RC.15 or later)
export OAUTH2_BASE_URL=http://localhost:9400/application/o/sample-app/ OAUTH2_CLIENT_ID=sample-app OAUTH2_CLIENT_SECRET=dev \
       OAUTH2_SCOPES="openid profile email offline_access" \
       OAUTH2_NATIVE_CLIENT_ID=sample-cli OAUTH2_NATIVE_CLIENT_BASE_URL=http://localhost:9400/application/o/sample-cli/
./mvnw -f qqq-sample-project/pom.xml compile exec:java -Dexec.mainClass=com.kingsrook.sampleapp.SampleJavalinServer -Dqqq.sample.port=8077

qqq login --server http://localhost:8077

Development

npm ci
npm run spec:merge -- <core openapi.yaml> <extension yaml>...   # or a server's /qqq/v1/openapi.json
npm run generate        # regenerate packages/ui-api-client/src/schema.ts
npm run build && npm test

Releases: push a tag v<client version>. The publish workflow publishes the three packages to GitHub Packages.

License

Apache-2.0

About

Reference CLI for the QQQ UI API (/qqq/v1), with the generated TypeScript client, a mock server and a conformance suite for client authors.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages