Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

## [Unreleased]

### 2026-03-17
- **Feat**: `--scopes` オプションを持つコマンドのヘルプに有効なスコープ一覧を NOTES セクションとして追加 (#73)
- **Docs**: README の Valid Scopes セクションを全18スコープに更新、包含関係と特殊スコープの説明を追加 (#73)
- **Fix**: `admin api-keys create` の例で誤ったスコープ形式 (`entities:read`) を正しい形式 (`read:entities`) に修正 (#73)

## [0.6.2] - 2026-03-17

### 2026-03-17
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,11 @@ When both a Bearer token and an API key are configured, both headers are sent (t

### Valid Scopes

`read:entities`, `write:entities`, `read:subscriptions`, `write:subscriptions`, `read:registrations`, `write:registrations`
`read:entities`, `write:entities`, `read:subscriptions`, `write:subscriptions`, `read:registrations`, `write:registrations`, `read:rules`, `write:rules`, `read:custom-data-models`, `write:custom-data-models`, `admin:users`, `admin:tenants`, `admin:policies`, `admin:oauth-clients`, `admin:api-keys`, `admin:metrics`

`write:X` implies `read:X`. `admin:X` implies both `read:X` and `write:X`.

Special scopes: `permanent` (no token expiry), `jwt` (JWT format token).

### Entity Type Restrictions

Expand Down
9 changes: 7 additions & 2 deletions src/commands/admin/api-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { withErrorHandler, createClient, resolveOptions, getFormat, outputRespon
import { loadConfig, saveConfig } from "../../config.js";
import { parseJsonInput } from "../../input.js";
import { printError, printWarning } from "../../output.js";
import { addExamples } from "../help.js";
import { addExamples, addNotes } from "../help.js";
import { SCOPES_HELP_NOTES } from "../../helpers.js";

function validateOrigins(body: unknown, opts: Record<string, unknown>): void {
// Validate origins if provided via flags
Expand Down Expand Up @@ -173,10 +174,12 @@ export function registerApiKeysCommand(parent: Command): void {
}),
);

addNotes(create, SCOPES_HELP_NOTES);

addExamples(create, [
{
description: "Create an API key with flags",
command: "geonic admin api-keys create --name my-key --scopes entities:read,entities:write --origins '*'",
command: "geonic admin api-keys create --name my-key --scopes read:entities,write:entities --origins '*'",
},
{
description: "Create an API key with DPoP required",
Expand Down Expand Up @@ -237,6 +240,8 @@ export function registerApiKeysCommand(parent: Command): void {
),
);

addNotes(update, SCOPES_HELP_NOTES);

addExamples(update, [
{
description: "Update an API key name",
Expand Down
16 changes: 16 additions & 0 deletions src/commands/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ interface Example {
}

const examplesMap = new WeakMap<Command, Example[]>();
const notesMap = new WeakMap<Command, string[]>();

export function addExamples(cmd: Command, examples: Example[]): void {
examplesMap.set(cmd, examples);
}

export function addNotes(cmd: Command, notes: string[]): void {
notesMap.set(cmd, notes);
}

function header(title: string): string {
return chalk.yellow.bold(title);
}
Expand Down Expand Up @@ -197,6 +202,17 @@ export function formatCommandDetails(
}
}

// NOTES
const notes = notesMap.get(cmd);
if (notes && notes.length > 0) {
lines.push("");
lines.push(header("NOTES"));
lines.push("");
for (const note of notes) {
lines.push(` ${note}`);
}
}

// GLOBAL PARAMETERS
lines.push("");
lines.push(formatGlobalParameters(program));
Expand Down
5 changes: 4 additions & 1 deletion src/commands/me-api-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { withErrorHandler, createClient, resolveOptions, getFormat, outputRespon
import { loadConfig, saveConfig } from "../config.js";
import { parseJsonInput } from "../input.js";
import { printError, printWarning } from "../output.js";
import { addExamples } from "./help.js";
import { addExamples, addNotes } from "./help.js";
import { SCOPES_HELP_NOTES } from "../helpers.js";

export function addMeApiKeysSubcommand(me: Command): void {
const apiKeys = me
Expand Down Expand Up @@ -127,6 +128,8 @@ export function addMeApiKeysSubcommand(me: Command): void {
}),
);

addNotes(create, SCOPES_HELP_NOTES);

addExamples(create, [
{
description: "Create an API key with flags",
Expand Down
5 changes: 4 additions & 1 deletion src/commands/me-oauth-clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { loadConfig, saveConfig, validateUrl } from "../config.js";
import { parseJsonInput } from "../input.js";
import { printSuccess, printError, printInfo, printWarning } from "../output.js";
import { clientCredentialsGrant } from "../oauth.js";
import { addExamples } from "./help.js";
import { addExamples, addNotes } from "./help.js";
import { SCOPES_HELP_NOTES } from "../helpers.js";

export function addMeOAuthClientsSubcommand(me: Command): void {
const oauthClients = me
Expand Down Expand Up @@ -106,6 +107,8 @@ export function addMeOAuthClientsSubcommand(me: Command): void {
}),
);

addNotes(create, SCOPES_HELP_NOTES);

addExamples(create, [
{
description: "Create an OAuth client with flags",
Expand Down
14 changes: 14 additions & 0 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ import { DryRunSignal, GdbClient, GdbClientError } from "./client.js";
import { printError, printOutput, printCount } from "./output.js";
import type { ClientResponse, GlobalOptions, OutputFormat } from "./types.js";

/**
* Valid scope values for --scopes option help text.
*/
export const SCOPES_HELP_NOTES = [
"Valid scopes:",
" read:entities, write:entities, read:subscriptions, write:subscriptions,",
" read:registrations, write:registrations, read:rules, write:rules,",
" read:custom-data-models, write:custom-data-models,",
" admin:users, admin:tenants, admin:policies, admin:oauth-clients,",
" admin:api-keys, admin:metrics",
"",
"write:X implies read:X. admin:X implies both read:X and write:X.",
];

/**
* Resolve merged options from config + CLI flags.
*/
Expand Down
2 changes: 2 additions & 0 deletions tests/admin-api-keys.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vi.mock("../src/helpers.js", () => ({
getFormat: vi.fn(),
outputResponse: vi.fn(),
withErrorHandler: (fn: (...args: unknown[]) => unknown) => fn,
SCOPES_HELP_NOTES: [],
resolveOptions: vi.fn().mockReturnValue({ profile: "default" }),
}));

Expand All @@ -25,6 +26,7 @@ vi.mock("../src/output.js", () => ({

vi.mock("../src/commands/help.js", () => ({
addExamples: vi.fn(),
addNotes: vi.fn(),
}));

vi.mock("../src/config.js", () => ({
Expand Down
2 changes: 2 additions & 0 deletions tests/admin-oauth-clients.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vi.mock("../src/helpers.js", () => ({
getFormat: vi.fn(),
outputResponse: vi.fn(),
withErrorHandler: (fn: (...args: unknown[]) => unknown) => fn,
SCOPES_HELP_NOTES: [],
resolveOptions: vi.fn(),
}));

Expand All @@ -25,6 +26,7 @@ vi.mock("../src/output.js", () => ({

vi.mock("../src/commands/help.js", () => ({
addExamples: vi.fn(),
addNotes: vi.fn(),
}));

import { createClient, getFormat, outputResponse } from "../src/helpers.js";
Expand Down
2 changes: 2 additions & 0 deletions tests/admin-policies.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vi.mock("../src/helpers.js", () => ({
getFormat: vi.fn(),
outputResponse: vi.fn(),
withErrorHandler: (fn: (...args: unknown[]) => unknown) => fn,
SCOPES_HELP_NOTES: [],
resolveOptions: vi.fn(),
}));

Expand All @@ -25,6 +26,7 @@ vi.mock("../src/output.js", () => ({

vi.mock("../src/commands/help.js", () => ({
addExamples: vi.fn(),
addNotes: vi.fn(),
}));

import { createClient, getFormat, outputResponse } from "../src/helpers.js";
Expand Down
2 changes: 2 additions & 0 deletions tests/admin-tenants.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vi.mock("../src/helpers.js", () => ({
getFormat: vi.fn(),
outputResponse: vi.fn(),
withErrorHandler: (fn: (...args: unknown[]) => unknown) => fn,
SCOPES_HELP_NOTES: [],
resolveOptions: vi.fn(),
}));

Expand All @@ -25,6 +26,7 @@ vi.mock("../src/output.js", () => ({

vi.mock("../src/commands/help.js", () => ({
addExamples: vi.fn(),
addNotes: vi.fn(),
}));

import { createClient, getFormat, outputResponse } from "../src/helpers.js";
Expand Down
2 changes: 2 additions & 0 deletions tests/admin-users.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vi.mock("../src/helpers.js", () => ({
getFormat: vi.fn(),
outputResponse: vi.fn(),
withErrorHandler: (fn: (...args: unknown[]) => unknown) => fn,
SCOPES_HELP_NOTES: [],
resolveOptions: vi.fn(),
}));

Expand All @@ -25,6 +26,7 @@ vi.mock("../src/output.js", () => ({

vi.mock("../src/commands/help.js", () => ({
addExamples: vi.fn(),
addNotes: vi.fn(),
}));

import { createClient, getFormat, outputResponse } from "../src/helpers.js";
Expand Down
2 changes: 2 additions & 0 deletions tests/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vi.mock("../src/helpers.js", () => ({
getFormat: vi.fn(),
outputResponse: vi.fn(),
withErrorHandler: (fn: (...args: unknown[]) => unknown) => fn,
SCOPES_HELP_NOTES: [],
resolveOptions: vi.fn(),
}));

Expand All @@ -25,6 +26,7 @@ vi.mock("../src/output.js", () => ({

vi.mock("../src/commands/help.js", () => ({
addExamples: vi.fn(),
addNotes: vi.fn(),
}));

vi.mock("../src/config.js", () => ({
Expand Down
1 change: 1 addition & 0 deletions tests/commands-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ vi.mock("../src/output.js", () => ({

vi.mock("../src/commands/help.js", () => ({
addExamples: vi.fn(),
addNotes: vi.fn(),
}));

import {
Expand Down
1 change: 1 addition & 0 deletions tests/commands-profile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ vi.mock("../src/output.js", () => ({

vi.mock("../src/commands/help.js", () => ({
addExamples: vi.fn(),
addNotes: vi.fn(),
}));

import {
Expand Down
2 changes: 2 additions & 0 deletions tests/entities.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vi.mock("../src/helpers.js", () => ({
getFormat: vi.fn(),
outputResponse: vi.fn(),
withErrorHandler: (fn: (...args: unknown[]) => unknown) => fn,
SCOPES_HELP_NOTES: [],
}));

vi.mock("../src/input.js", () => ({
Expand All @@ -24,6 +25,7 @@ vi.mock("../src/output.js", () => ({

vi.mock("../src/commands/help.js", () => ({
addExamples: vi.fn(),
addNotes: vi.fn(),
}));

vi.mock("../src/commands/attrs.js", () => ({
Expand Down
2 changes: 2 additions & 0 deletions tests/health.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vi.mock("../src/helpers.js", () => ({
getFormat: vi.fn(),
outputResponse: vi.fn(),
withErrorHandler: (fn: (...args: unknown[]) => unknown) => fn,
SCOPES_HELP_NOTES: [],
resolveOptions: vi.fn(),
}));

Expand All @@ -21,6 +22,7 @@ vi.mock("../src/output.js", () => ({

vi.mock("../src/commands/help.js", () => ({
addExamples: vi.fn(),
addNotes: vi.fn(),
}));

vi.mock("node:module", () => ({
Expand Down
2 changes: 2 additions & 0 deletions tests/me-api-keys.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vi.mock("../src/helpers.js", () => ({
getFormat: vi.fn(),
outputResponse: vi.fn(),
withErrorHandler: (fn: (...args: unknown[]) => unknown) => fn,
SCOPES_HELP_NOTES: [],
resolveOptions: vi.fn().mockReturnValue({ profile: "default" }),
}));

Expand All @@ -25,6 +26,7 @@ vi.mock("../src/output.js", () => ({

vi.mock("../src/commands/help.js", () => ({
addExamples: vi.fn(),
addNotes: vi.fn(),
}));

vi.mock("../src/config.js", () => ({
Expand Down
2 changes: 2 additions & 0 deletions tests/me-oauth-clients.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vi.mock("../src/helpers.js", () => ({
getFormat: vi.fn(),
outputResponse: vi.fn(),
withErrorHandler: (fn: (...args: unknown[]) => unknown) => fn,
SCOPES_HELP_NOTES: [],
resolveOptions: vi.fn(),
}));

Expand All @@ -25,6 +26,7 @@ vi.mock("../src/output.js", () => ({

vi.mock("../src/commands/help.js", () => ({
addExamples: vi.fn(),
addNotes: vi.fn(),
}));

vi.mock("../src/config.js", () => ({
Expand Down
2 changes: 2 additions & 0 deletions tests/setup-command-mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ vi.mock("../src/helpers.js", () => ({
getFormat: vi.fn(),
outputResponse: vi.fn(),
withErrorHandler: (fn: (...args: unknown[]) => unknown) => fn,
SCOPES_HELP_NOTES: [],
}));

vi.mock("../src/input.js", () => ({
Expand All @@ -32,4 +33,5 @@ vi.mock("../src/output.js", () => ({

vi.mock("../src/commands/help.js", () => ({
addExamples: vi.fn(),
addNotes: vi.fn(),
}));
2 changes: 2 additions & 0 deletions tests/snapshots.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vi.mock("../src/helpers.js", () => ({
getFormat: vi.fn(),
outputResponse: vi.fn(),
withErrorHandler: (fn: (...args: unknown[]) => unknown) => fn,
SCOPES_HELP_NOTES: [],
}));

vi.mock("../src/output.js", () => ({
Expand All @@ -20,6 +21,7 @@ vi.mock("../src/output.js", () => ({

vi.mock("../src/commands/help.js", () => ({
addExamples: vi.fn(),
addNotes: vi.fn(),
}));

import { createClient, getFormat, outputResponse } from "../src/helpers.js";
Expand Down
2 changes: 2 additions & 0 deletions tests/temporal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vi.mock("../src/helpers.js", () => ({
getFormat: vi.fn(),
outputResponse: vi.fn(),
withErrorHandler: (fn: (...args: unknown[]) => unknown) => fn,
SCOPES_HELP_NOTES: [],
resolveOptions: vi.fn(),
}));

Expand All @@ -25,6 +26,7 @@ vi.mock("../src/output.js", () => ({

vi.mock("../src/commands/help.js", () => ({
addExamples: vi.fn(),
addNotes: vi.fn(),
}));

import { createClient, getFormat, outputResponse } from "../src/helpers.js";
Expand Down
2 changes: 2 additions & 0 deletions tests/types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vi.mock("../src/helpers.js", () => ({
getFormat: vi.fn(),
outputResponse: vi.fn(),
withErrorHandler: (fn: (...args: unknown[]) => unknown) => fn,
SCOPES_HELP_NOTES: [],
}));

vi.mock("../src/output.js", () => ({
Expand All @@ -20,6 +21,7 @@ vi.mock("../src/output.js", () => ({

vi.mock("../src/commands/help.js", () => ({
addExamples: vi.fn(),
addNotes: vi.fn(),
}));

import { createClient, getFormat, outputResponse } from "../src/helpers.js";
Expand Down
Loading