@geolonia/geonicdb-cli 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +544 -172
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -25,6 +25,7 @@ function migrateV1ToV2(data) {
|
|
|
25
25
|
const knownKeys = [
|
|
26
26
|
"url",
|
|
27
27
|
"service",
|
|
28
|
+
"tenantId",
|
|
28
29
|
"token",
|
|
29
30
|
"refreshToken",
|
|
30
31
|
"format",
|
|
@@ -592,7 +593,7 @@ function registerConfigCommand(program2) {
|
|
|
592
593
|
command: "geonic config get url --profile staging"
|
|
593
594
|
}
|
|
594
595
|
]);
|
|
595
|
-
const list = config.command("list").description("List all config values").action((...args) => {
|
|
596
|
+
const list = config.command("list").description("List all config values for the active (or specified) profile").action((...args) => {
|
|
596
597
|
const cmd = args[args.length - 1];
|
|
597
598
|
const profile = cmd.optsWithGlobals().profile;
|
|
598
599
|
const all = loadConfig(profile);
|
|
@@ -606,9 +607,13 @@ function registerConfigCommand(program2) {
|
|
|
606
607
|
{
|
|
607
608
|
description: "List all configuration values",
|
|
608
609
|
command: "geonic config list"
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
description: "List config for a specific profile",
|
|
613
|
+
command: "geonic config list --profile staging"
|
|
609
614
|
}
|
|
610
615
|
]);
|
|
611
|
-
const del = config.command("delete").description("
|
|
616
|
+
const del = config.command("delete").description("Remove a config key from the active (or specified) profile").argument("<key>", "Configuration key").action((...args) => {
|
|
612
617
|
const cmd = args[args.length - 1];
|
|
613
618
|
const key = args[0];
|
|
614
619
|
const profile = cmd.optsWithGlobals().profile;
|
|
@@ -617,8 +622,16 @@ function registerConfigCommand(program2) {
|
|
|
617
622
|
});
|
|
618
623
|
addExamples(del, [
|
|
619
624
|
{
|
|
620
|
-
description: "
|
|
625
|
+
description: "Remove the saved server URL",
|
|
621
626
|
command: "geonic config delete url"
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
description: "Clear the saved authentication token",
|
|
630
|
+
command: "geonic config delete token"
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
description: "Remove API key from a specific profile",
|
|
634
|
+
command: "geonic config delete apiKey --profile staging"
|
|
622
635
|
}
|
|
623
636
|
]);
|
|
624
637
|
}
|
|
@@ -673,6 +686,7 @@ var GdbClient = class _GdbClient {
|
|
|
673
686
|
clientId;
|
|
674
687
|
clientSecret;
|
|
675
688
|
onTokenRefresh;
|
|
689
|
+
onBeforeRefresh;
|
|
676
690
|
verbose;
|
|
677
691
|
dryRun;
|
|
678
692
|
refreshPromise;
|
|
@@ -685,6 +699,7 @@ var GdbClient = class _GdbClient {
|
|
|
685
699
|
this.clientId = options.clientId;
|
|
686
700
|
this.clientSecret = options.clientSecret;
|
|
687
701
|
this.onTokenRefresh = options.onTokenRefresh;
|
|
702
|
+
this.onBeforeRefresh = options.onBeforeRefresh;
|
|
688
703
|
this.verbose = options.verbose ?? false;
|
|
689
704
|
this.dryRun = options.dryRun ?? false;
|
|
690
705
|
}
|
|
@@ -813,6 +828,17 @@ var GdbClient = class _GdbClient {
|
|
|
813
828
|
}
|
|
814
829
|
}
|
|
815
830
|
async doRefresh() {
|
|
831
|
+
if (this.onBeforeRefresh) {
|
|
832
|
+
const latest = this.onBeforeRefresh();
|
|
833
|
+
if (latest.token && latest.token !== this.token) {
|
|
834
|
+
this.token = latest.token;
|
|
835
|
+
if (latest.refreshToken) this.refreshToken = latest.refreshToken;
|
|
836
|
+
return true;
|
|
837
|
+
}
|
|
838
|
+
if (latest.refreshToken) {
|
|
839
|
+
this.refreshToken = latest.refreshToken;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
816
842
|
if (this.refreshToken) {
|
|
817
843
|
try {
|
|
818
844
|
const url = this.buildUrl("/auth/refresh");
|
|
@@ -994,6 +1020,10 @@ function createClient(cmd) {
|
|
|
994
1020
|
if (refreshToken) cfg.refreshToken = refreshToken;
|
|
995
1021
|
saveConfig(cfg, opts.profile);
|
|
996
1022
|
},
|
|
1023
|
+
onBeforeRefresh: usingCliToken ? void 0 : () => {
|
|
1024
|
+
const cfg = loadConfig(opts.profile);
|
|
1025
|
+
return { token: cfg.token, refreshToken: cfg.refreshToken };
|
|
1026
|
+
},
|
|
997
1027
|
verbose: opts.verbose,
|
|
998
1028
|
dryRun: opts.dryRun
|
|
999
1029
|
});
|
|
@@ -1141,7 +1171,8 @@ async function promptTenantSelection(tenants, currentTenantId) {
|
|
|
1141
1171
|
const t = tenants[i];
|
|
1142
1172
|
const current = t.tenantId === currentTenantId ? " \u2190 current" : "";
|
|
1143
1173
|
const marker = t.tenantId === currentTenantId ? " *" : " ";
|
|
1144
|
-
|
|
1174
|
+
const label = t.name ? `${t.name} (${t.tenantId})` : t.tenantId;
|
|
1175
|
+
console.log(`${marker} ${i + 1}) ${label} [${t.role}]${current}`);
|
|
1145
1176
|
}
|
|
1146
1177
|
for (; ; ) {
|
|
1147
1178
|
const answer = await rl.question("\nSelect tenant number (Enter to keep current): ");
|
|
@@ -1328,6 +1359,10 @@ function addMeOAuthClientsSubcommand(me) {
|
|
|
1328
1359
|
{
|
|
1329
1360
|
description: "List your OAuth clients",
|
|
1330
1361
|
command: "geonic me oauth-clients list"
|
|
1362
|
+
},
|
|
1363
|
+
{
|
|
1364
|
+
description: "List in table format for a quick overview",
|
|
1365
|
+
command: "geonic me oauth-clients list --format table"
|
|
1331
1366
|
}
|
|
1332
1367
|
]);
|
|
1333
1368
|
const create = oauthClients.command("create [json]").description("Create a new OAuth client").option("--name <name>", "Client name").option("--policy <policyId>", "Policy ID to attach").option("--save", "Save credentials to config for automatic re-authentication").action(
|
|
@@ -1448,7 +1483,7 @@ function addMeOAuthClientsSubcommand(me) {
|
|
|
1448
1483
|
command: "geonic me oauth-clients update <client-id> --inactive"
|
|
1449
1484
|
}
|
|
1450
1485
|
]);
|
|
1451
|
-
const del = oauthClients.command("delete <id>").description("Delete an OAuth client").action(
|
|
1486
|
+
const del = oauthClients.command("delete <id>").description("Delete an OAuth client and revoke its credentials").action(
|
|
1452
1487
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
1453
1488
|
const client = createClient(cmd);
|
|
1454
1489
|
await client.rawRequest(
|
|
@@ -1460,11 +1495,15 @@ function addMeOAuthClientsSubcommand(me) {
|
|
|
1460
1495
|
);
|
|
1461
1496
|
addExamples(del, [
|
|
1462
1497
|
{
|
|
1463
|
-
description: "Delete an OAuth client",
|
|
1498
|
+
description: "Delete an OAuth client by ID",
|
|
1464
1499
|
command: "geonic me oauth-clients delete <client-id>"
|
|
1500
|
+
},
|
|
1501
|
+
{
|
|
1502
|
+
description: "Revoke a compromised client",
|
|
1503
|
+
command: "geonic me oauth-clients delete abc123-def456"
|
|
1465
1504
|
}
|
|
1466
1505
|
]);
|
|
1467
|
-
const regenerateSecret = oauthClients.command("regenerate-secret <clientId>").description("Regenerate the client secret
|
|
1506
|
+
const regenerateSecret = oauthClients.command("regenerate-secret <clientId>").description("Regenerate the client secret \u2014 the old secret is immediately invalidated").action(
|
|
1468
1507
|
withErrorHandler(async (clientId, _opts, cmd) => {
|
|
1469
1508
|
const client = createClient(cmd);
|
|
1470
1509
|
const format = getFormat(cmd);
|
|
@@ -1481,6 +1520,10 @@ function addMeOAuthClientsSubcommand(me) {
|
|
|
1481
1520
|
{
|
|
1482
1521
|
description: "Regenerate client secret",
|
|
1483
1522
|
command: "geonic me oauth-clients regenerate-secret <client-id>"
|
|
1523
|
+
},
|
|
1524
|
+
{
|
|
1525
|
+
description: "Rotate secret after a security incident",
|
|
1526
|
+
command: "geonic me oauth-clients regenerate-secret abc123-def456"
|
|
1484
1527
|
}
|
|
1485
1528
|
]);
|
|
1486
1529
|
}
|
|
@@ -1541,6 +1584,10 @@ function addMeApiKeysSubcommand(me) {
|
|
|
1541
1584
|
{
|
|
1542
1585
|
description: "List your API keys",
|
|
1543
1586
|
command: "geonic me api-keys list"
|
|
1587
|
+
},
|
|
1588
|
+
{
|
|
1589
|
+
description: "List in table format for a quick overview",
|
|
1590
|
+
command: "geonic me api-keys list --format table"
|
|
1544
1591
|
}
|
|
1545
1592
|
]);
|
|
1546
1593
|
const create = apiKeys.command("create [json]").description("Create a new API key").option("--name <name>", "Key name").option("--policy <policyId>", "Policy ID to attach").option("--origins <origins>", "Allowed origins (comma-separated)").option("--rate-limit <n>", "Rate limit per minute").option("--dpop-required", "Require DPoP token binding").option("--save", "Save the API key to config for automatic use").action(
|
|
@@ -1721,7 +1768,7 @@ function addMeApiKeysSubcommand(me) {
|
|
|
1721
1768
|
command: `geonic me api-keys update <key-id> '{"name":"new-name","rateLimit":{"perMinute":60}}'`
|
|
1722
1769
|
}
|
|
1723
1770
|
]);
|
|
1724
|
-
const del = apiKeys.command("delete <keyId>").description("Delete an API key").action(
|
|
1771
|
+
const del = apiKeys.command("delete <keyId>").description("Delete an API key \u2014 immediately revokes access for any client using it").action(
|
|
1725
1772
|
withErrorHandler(async (keyId, _opts, cmd) => {
|
|
1726
1773
|
const client = createClient(cmd);
|
|
1727
1774
|
await client.rawRequest(
|
|
@@ -1733,8 +1780,12 @@ function addMeApiKeysSubcommand(me) {
|
|
|
1733
1780
|
);
|
|
1734
1781
|
addExamples(del, [
|
|
1735
1782
|
{
|
|
1736
|
-
description: "Delete an API key",
|
|
1783
|
+
description: "Delete an API key by ID",
|
|
1737
1784
|
command: "geonic me api-keys delete <key-id>"
|
|
1785
|
+
},
|
|
1786
|
+
{
|
|
1787
|
+
description: "Revoke a leaked or unused key",
|
|
1788
|
+
command: "geonic me api-keys delete abc123-def456"
|
|
1738
1789
|
}
|
|
1739
1790
|
]);
|
|
1740
1791
|
}
|
|
@@ -1754,9 +1805,13 @@ function addMePoliciesSubcommand(me) {
|
|
|
1754
1805
|
{
|
|
1755
1806
|
description: "List your personal policies",
|
|
1756
1807
|
command: "geonic me policies list"
|
|
1808
|
+
},
|
|
1809
|
+
{
|
|
1810
|
+
description: "List in table format for a quick overview",
|
|
1811
|
+
command: "geonic me policies list --format table"
|
|
1757
1812
|
}
|
|
1758
1813
|
]);
|
|
1759
|
-
const get = policies.command("get <policyId>").description("Get a personal policy by ID").action(
|
|
1814
|
+
const get = policies.command("get <policyId>").description("Get a personal policy by ID to inspect its XACML rules and target resources").action(
|
|
1760
1815
|
withErrorHandler(async (policyId, _opts, cmd) => {
|
|
1761
1816
|
const client = createClient(cmd);
|
|
1762
1817
|
const format = getFormat(cmd);
|
|
@@ -1771,9 +1826,13 @@ function addMePoliciesSubcommand(me) {
|
|
|
1771
1826
|
{
|
|
1772
1827
|
description: "Get a personal policy by ID",
|
|
1773
1828
|
command: "geonic me policies get <policy-id>"
|
|
1829
|
+
},
|
|
1830
|
+
{
|
|
1831
|
+
description: "Inspect policy rules and permitted actions",
|
|
1832
|
+
command: "geonic me policies get my-readonly --format table"
|
|
1774
1833
|
}
|
|
1775
1834
|
]);
|
|
1776
|
-
const create = policies.command("create [json]").description(
|
|
1835
|
+
const create = policies.command("create [json]").summary("Create a personal XACML policy").description(
|
|
1777
1836
|
`Create a personal XACML policy
|
|
1778
1837
|
|
|
1779
1838
|
Constraints (enforced server-side):
|
|
@@ -1864,7 +1923,7 @@ Example \u2014 GET-only policy for /v2/**:
|
|
|
1864
1923
|
command: "geonic me policies update <policy-id> @patch.json"
|
|
1865
1924
|
}
|
|
1866
1925
|
]);
|
|
1867
|
-
const del = policies.command("delete <policyId>").description("Delete a personal policy").action(
|
|
1926
|
+
const del = policies.command("delete <policyId>").description("Delete a personal policy \u2014 any API key or OAuth client bound to it loses access granted by this policy").action(
|
|
1868
1927
|
withErrorHandler(async (policyId, _opts, cmd) => {
|
|
1869
1928
|
const client = createClient(cmd);
|
|
1870
1929
|
await client.rawRequest(
|
|
@@ -1876,8 +1935,12 @@ Example \u2014 GET-only policy for /v2/**:
|
|
|
1876
1935
|
);
|
|
1877
1936
|
addExamples(del, [
|
|
1878
1937
|
{
|
|
1879
|
-
description: "Delete a personal policy",
|
|
1938
|
+
description: "Delete a personal policy by ID",
|
|
1880
1939
|
command: "geonic me policies delete <policy-id>"
|
|
1940
|
+
},
|
|
1941
|
+
{
|
|
1942
|
+
description: "Remove a policy (unbind from API keys/OAuth clients first)",
|
|
1943
|
+
command: "geonic me policies delete my-readonly"
|
|
1881
1944
|
}
|
|
1882
1945
|
]);
|
|
1883
1946
|
}
|
|
@@ -1935,8 +1998,10 @@ function createLoginCommand() {
|
|
|
1935
1998
|
const password = await promptPassword();
|
|
1936
1999
|
const client = createClient(cmd);
|
|
1937
2000
|
const body = { email, password };
|
|
1938
|
-
|
|
1939
|
-
|
|
2001
|
+
const requestTenantId = loginOpts.tenantId;
|
|
2002
|
+
const serviceFlag = globalOpts.service;
|
|
2003
|
+
if (requestTenantId) {
|
|
2004
|
+
body.tenantId = requestTenantId;
|
|
1940
2005
|
}
|
|
1941
2006
|
const response = await client.rawRequest("POST", "/auth/login", {
|
|
1942
2007
|
body,
|
|
@@ -1951,11 +2016,27 @@ function createLoginCommand() {
|
|
|
1951
2016
|
}
|
|
1952
2017
|
const availableTenants = data.availableTenants;
|
|
1953
2018
|
let finalTenantId = data.tenantId;
|
|
1954
|
-
if (availableTenants && availableTenants.length > 1 && !
|
|
1955
|
-
|
|
1956
|
-
if (
|
|
2019
|
+
if (availableTenants && availableTenants.length > 1 && !requestTenantId) {
|
|
2020
|
+
let resolvedTenantId;
|
|
2021
|
+
if (serviceFlag) {
|
|
2022
|
+
const match = availableTenants.find(
|
|
2023
|
+
(t) => t.name === serviceFlag || t.tenantId === serviceFlag
|
|
2024
|
+
);
|
|
2025
|
+
if (match) {
|
|
2026
|
+
resolvedTenantId = match.tenantId;
|
|
2027
|
+
} else {
|
|
2028
|
+
printError(
|
|
2029
|
+
`Tenant "${serviceFlag}" not found. Available: ${availableTenants.map((t) => t.name ?? t.tenantId).join(", ")}`
|
|
2030
|
+
);
|
|
2031
|
+
process.exit(1);
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
if (!resolvedTenantId) {
|
|
2035
|
+
resolvedTenantId = await promptTenantSelection(availableTenants, finalTenantId);
|
|
2036
|
+
}
|
|
2037
|
+
if (resolvedTenantId && resolvedTenantId !== finalTenantId) {
|
|
1957
2038
|
const reloginResponse = await client.rawRequest("POST", "/auth/login", {
|
|
1958
|
-
body: { email, password, tenantId:
|
|
2039
|
+
body: { email, password, tenantId: resolvedTenantId },
|
|
1959
2040
|
skipTenantHeader: true
|
|
1960
2041
|
});
|
|
1961
2042
|
const reloginData = reloginResponse.data;
|
|
@@ -1966,7 +2047,7 @@ function createLoginCommand() {
|
|
|
1966
2047
|
}
|
|
1967
2048
|
token = newToken;
|
|
1968
2049
|
refreshToken = reloginData.refreshToken;
|
|
1969
|
-
finalTenantId =
|
|
2050
|
+
finalTenantId = resolvedTenantId;
|
|
1970
2051
|
}
|
|
1971
2052
|
}
|
|
1972
2053
|
const config = loadConfig(globalOpts.profile);
|
|
@@ -1978,16 +2059,28 @@ function createLoginCommand() {
|
|
|
1978
2059
|
}
|
|
1979
2060
|
if (finalTenantId) {
|
|
1980
2061
|
config.service = finalTenantId;
|
|
2062
|
+
config.tenantId = finalTenantId;
|
|
1981
2063
|
} else {
|
|
1982
2064
|
delete config.service;
|
|
2065
|
+
delete config.tenantId;
|
|
2066
|
+
}
|
|
2067
|
+
if (availableTenants && availableTenants.length > 0) {
|
|
2068
|
+
config.availableTenants = availableTenants.map((t) => ({
|
|
2069
|
+
tenantId: t.tenantId,
|
|
2070
|
+
...t.name ? { name: t.name } : {},
|
|
2071
|
+
role: t.role
|
|
2072
|
+
}));
|
|
2073
|
+
} else {
|
|
2074
|
+
delete config.availableTenants;
|
|
1983
2075
|
}
|
|
1984
2076
|
saveConfig(config, globalOpts.profile);
|
|
1985
|
-
|
|
2077
|
+
const tenantLabel = finalTenantId ? ` (tenant: ${availableTenants?.find((t) => t.tenantId === finalTenantId)?.name ?? finalTenantId})` : "";
|
|
2078
|
+
printSuccess(`Login successful${tenantLabel}. Token saved to config.`);
|
|
1986
2079
|
})
|
|
1987
2080
|
);
|
|
1988
2081
|
}
|
|
1989
2082
|
function createLogoutCommand() {
|
|
1990
|
-
return new Command("logout").description("Clear saved authentication token").action(
|
|
2083
|
+
return new Command("logout").description("Clear saved authentication token and notify the server").action(
|
|
1991
2084
|
withErrorHandler(async (...args) => {
|
|
1992
2085
|
const cmd = args[args.length - 1];
|
|
1993
2086
|
const globalOpts = resolveOptions(cmd);
|
|
@@ -2060,7 +2153,7 @@ async function fetchNonce(baseUrl, apiKey) {
|
|
|
2060
2153
|
return await response.json();
|
|
2061
2154
|
}
|
|
2062
2155
|
function createNonceCommand() {
|
|
2063
|
-
return new Command("nonce").description("
|
|
2156
|
+
return new Command("nonce").description("Request a nonce and Proof-of-Work challenge (step 1 of API key to JWT exchange)").option("--api-key <key>", "API key to get nonce for").action(
|
|
2064
2157
|
withErrorHandler(async (...args) => {
|
|
2065
2158
|
const cmd = args[args.length - 1];
|
|
2066
2159
|
const nonceOpts = cmd.opts();
|
|
@@ -2102,7 +2195,7 @@ function solvePoW(challenge, difficulty) {
|
|
|
2102
2195
|
throw new Error(`PoW could not be solved within ${MAX_POW_ITERATIONS} iterations`);
|
|
2103
2196
|
}
|
|
2104
2197
|
function createTokenExchangeCommand() {
|
|
2105
|
-
return new Command("token-exchange").description("Exchange API key for a session JWT
|
|
2198
|
+
return new Command("token-exchange").description("Exchange an API key for a session JWT (fetches nonce, solves PoW, returns token)").option("--api-key <key>", "API key to exchange").option("--save", "Save the obtained token to profile config").action(
|
|
2106
2199
|
withErrorHandler(async (...args) => {
|
|
2107
2200
|
const cmd = args[args.length - 1];
|
|
2108
2201
|
const exchangeOpts = cmd.opts();
|
|
@@ -2166,8 +2259,12 @@ function registerAuthCommands(program2) {
|
|
|
2166
2259
|
command: "geonic auth login --client-credentials --client-id MY_ID --client-secret MY_SECRET"
|
|
2167
2260
|
},
|
|
2168
2261
|
{
|
|
2169
|
-
description: "Login to a specific tenant",
|
|
2262
|
+
description: "Login to a specific tenant by ID",
|
|
2170
2263
|
command: "geonic auth login --tenant-id my-tenant"
|
|
2264
|
+
},
|
|
2265
|
+
{
|
|
2266
|
+
description: "Login to a tenant by name",
|
|
2267
|
+
command: "geonic auth login -s demo_smartcity"
|
|
2171
2268
|
}
|
|
2172
2269
|
]);
|
|
2173
2270
|
auth.addCommand(login);
|
|
@@ -2176,6 +2273,10 @@ function registerAuthCommands(program2) {
|
|
|
2176
2273
|
{
|
|
2177
2274
|
description: "Clear saved authentication token",
|
|
2178
2275
|
command: "geonic auth logout"
|
|
2276
|
+
},
|
|
2277
|
+
{
|
|
2278
|
+
description: "Logout from a specific profile",
|
|
2279
|
+
command: "geonic auth logout --profile staging"
|
|
2179
2280
|
}
|
|
2180
2281
|
]);
|
|
2181
2282
|
auth.addCommand(logout);
|
|
@@ -2184,14 +2285,26 @@ function registerAuthCommands(program2) {
|
|
|
2184
2285
|
{
|
|
2185
2286
|
description: "Get a nonce for API key authentication",
|
|
2186
2287
|
command: "geonic auth nonce --api-key gdb_abcdef..."
|
|
2288
|
+
},
|
|
2289
|
+
{
|
|
2290
|
+
description: "Use a pre-configured API key",
|
|
2291
|
+
command: "geonic auth nonce"
|
|
2187
2292
|
}
|
|
2188
2293
|
]);
|
|
2189
2294
|
auth.addCommand(nonce);
|
|
2190
2295
|
const tokenExchange = createTokenExchangeCommand();
|
|
2191
2296
|
addExamples(tokenExchange, [
|
|
2192
2297
|
{
|
|
2193
|
-
description: "Exchange API key for a JWT and save it",
|
|
2298
|
+
description: "Exchange API key for a JWT and save it to the active profile",
|
|
2194
2299
|
command: "geonic auth token-exchange --api-key gdb_abcdef... --save"
|
|
2300
|
+
},
|
|
2301
|
+
{
|
|
2302
|
+
description: "Exchange and print the JWT without saving",
|
|
2303
|
+
command: "geonic auth token-exchange --api-key gdb_abcdef..."
|
|
2304
|
+
},
|
|
2305
|
+
{
|
|
2306
|
+
description: "Use a pre-configured API key and save the resulting token",
|
|
2307
|
+
command: "geonic auth token-exchange --save"
|
|
2195
2308
|
}
|
|
2196
2309
|
]);
|
|
2197
2310
|
auth.addCommand(tokenExchange);
|
|
@@ -2228,8 +2341,8 @@ function registerAuthCommands(program2) {
|
|
|
2228
2341
|
|
|
2229
2342
|
// src/commands/profile.ts
|
|
2230
2343
|
function registerProfileCommands(program2) {
|
|
2231
|
-
const profile = program2.command("profile").description("Manage connection profiles");
|
|
2232
|
-
const list = profile.command("list").description("List all profiles").action(() => {
|
|
2344
|
+
const profile = program2.command("profile").description("Manage connection profiles (each profile stores its own URL, token, and tenant)");
|
|
2345
|
+
const list = profile.command("list").description("List all profiles (active profile marked with *)").action(() => {
|
|
2233
2346
|
const profiles = listProfiles();
|
|
2234
2347
|
for (const p of profiles) {
|
|
2235
2348
|
const marker = p.active ? " *" : "";
|
|
@@ -2242,22 +2355,57 @@ function registerProfileCommands(program2) {
|
|
|
2242
2355
|
command: "geonic profile list"
|
|
2243
2356
|
}
|
|
2244
2357
|
]);
|
|
2245
|
-
const use = profile.command("use <name>").description("Switch active profile").action((name) => {
|
|
2358
|
+
const use = profile.command("use <name>").description("Switch active profile (auto-refreshes expired tokens)").action(async (name) => {
|
|
2246
2359
|
try {
|
|
2247
2360
|
setCurrentProfile(name);
|
|
2248
|
-
printSuccess(`Switched to profile "${name}".`);
|
|
2249
2361
|
} catch (err) {
|
|
2250
2362
|
printError(err.message);
|
|
2251
2363
|
process.exit(1);
|
|
2252
2364
|
}
|
|
2365
|
+
const config = loadConfig(name);
|
|
2366
|
+
const tenantLabel = config.tenantId ? ` (tenant: ${config.availableTenants?.find((t) => t.tenantId === config.tenantId)?.name ?? config.tenantId})` : "";
|
|
2367
|
+
if (config.token && config.refreshToken && config.url) {
|
|
2368
|
+
const status = getTokenStatus(config.token);
|
|
2369
|
+
if (status.isExpired || status.isExpiringSoon) {
|
|
2370
|
+
try {
|
|
2371
|
+
const baseUrl = validateUrl(config.url);
|
|
2372
|
+
const url = new URL("/auth/refresh", baseUrl).toString();
|
|
2373
|
+
const response = await fetch(url, {
|
|
2374
|
+
method: "POST",
|
|
2375
|
+
headers: { "Content-Type": "application/json" },
|
|
2376
|
+
body: JSON.stringify({ refreshToken: config.refreshToken })
|
|
2377
|
+
});
|
|
2378
|
+
if (response.ok) {
|
|
2379
|
+
const data = await response.json();
|
|
2380
|
+
const newToken = data.accessToken ?? data.token;
|
|
2381
|
+
const newRefreshToken = data.refreshToken;
|
|
2382
|
+
if (newToken) {
|
|
2383
|
+
config.token = newToken;
|
|
2384
|
+
if (newRefreshToken) config.refreshToken = newRefreshToken;
|
|
2385
|
+
saveConfig(config, name);
|
|
2386
|
+
printSuccess(`Switched to profile "${name}"${tenantLabel}. Token refreshed.`);
|
|
2387
|
+
return;
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
printWarning("Token refresh failed. You may need to re-login.");
|
|
2391
|
+
} catch {
|
|
2392
|
+
printWarning("Token refresh failed. You may need to re-login.");
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
printSuccess(`Switched to profile "${name}"${tenantLabel}.`);
|
|
2253
2397
|
});
|
|
2254
2398
|
addExamples(use, [
|
|
2255
2399
|
{
|
|
2256
2400
|
description: "Switch to staging profile",
|
|
2257
2401
|
command: "geonic profile use staging"
|
|
2402
|
+
},
|
|
2403
|
+
{
|
|
2404
|
+
description: "Switch to production profile",
|
|
2405
|
+
command: "geonic profile use production"
|
|
2258
2406
|
}
|
|
2259
2407
|
]);
|
|
2260
|
-
const profileCreate = profile.command("create <name>").description("Create a new profile").action((name) => {
|
|
2408
|
+
const profileCreate = profile.command("create <name>").description("Create a new named profile for a separate environment or tenant").action((name) => {
|
|
2261
2409
|
try {
|
|
2262
2410
|
createProfile(name);
|
|
2263
2411
|
printSuccess(`Profile "${name}" created.`);
|
|
@@ -2270,9 +2418,13 @@ function registerProfileCommands(program2) {
|
|
|
2270
2418
|
{
|
|
2271
2419
|
description: "Create a new profile for staging",
|
|
2272
2420
|
command: "geonic profile create staging"
|
|
2421
|
+
},
|
|
2422
|
+
{
|
|
2423
|
+
description: "Create a profile for a different tenant",
|
|
2424
|
+
command: "geonic profile create tenant-b"
|
|
2273
2425
|
}
|
|
2274
2426
|
]);
|
|
2275
|
-
const del = profile.command("delete <name>").description("Delete a profile").action((name) => {
|
|
2427
|
+
const del = profile.command("delete <name>").description("Delete a profile and its stored configuration").action((name) => {
|
|
2276
2428
|
try {
|
|
2277
2429
|
deleteProfile(name);
|
|
2278
2430
|
printSuccess(`Profile "${name}" deleted.`);
|
|
@@ -2283,11 +2435,11 @@ function registerProfileCommands(program2) {
|
|
|
2283
2435
|
});
|
|
2284
2436
|
addExamples(del, [
|
|
2285
2437
|
{
|
|
2286
|
-
description: "Delete
|
|
2438
|
+
description: "Delete the staging profile",
|
|
2287
2439
|
command: "geonic profile delete staging"
|
|
2288
2440
|
}
|
|
2289
2441
|
]);
|
|
2290
|
-
const show = profile.command("show [name]").description("Show profile settings").action((name) => {
|
|
2442
|
+
const show = profile.command("show [name]").description("Show profile settings (URL, tenant, token status, etc.)").action((name) => {
|
|
2291
2443
|
const profileName = name ?? getCurrentProfile();
|
|
2292
2444
|
const config = loadConfig(profileName);
|
|
2293
2445
|
const entries = Object.entries(config).filter(([, v]) => v !== void 0);
|
|
@@ -2298,6 +2450,13 @@ function registerProfileCommands(program2) {
|
|
|
2298
2450
|
for (const [key, value] of entries) {
|
|
2299
2451
|
if ((key === "token" || key === "refreshToken" || key === "apiKey") && typeof value === "string") {
|
|
2300
2452
|
console.log(`${key}: ***`);
|
|
2453
|
+
} else if (key === "availableTenants" && Array.isArray(value)) {
|
|
2454
|
+
console.log(`${key}:`);
|
|
2455
|
+
for (const t of value) {
|
|
2456
|
+
const label = t.name ? `${t.name} (${t.tenantId})` : t.tenantId;
|
|
2457
|
+
const current = t.tenantId === config.tenantId ? " \u2190 current" : "";
|
|
2458
|
+
console.log(` - ${label} [${t.role}]${current}`);
|
|
2459
|
+
}
|
|
2301
2460
|
} else {
|
|
2302
2461
|
console.log(`${key}: ${value}`);
|
|
2303
2462
|
}
|
|
@@ -2317,7 +2476,7 @@ function registerProfileCommands(program2) {
|
|
|
2317
2476
|
|
|
2318
2477
|
// src/commands/attrs.ts
|
|
2319
2478
|
function addAttrsSubcommands(attrs) {
|
|
2320
|
-
const list = attrs.command("list").description("List all attributes of an entity").argument("<entityId>", "Entity ID").action(
|
|
2479
|
+
const list = attrs.command("list").description("List all attributes of an entity, returning each attribute's type, value, and metadata").argument("<entityId>", "Entity ID").action(
|
|
2321
2480
|
withErrorHandler(
|
|
2322
2481
|
async (entityId, _opts, cmd) => {
|
|
2323
2482
|
const client = createClient(cmd);
|
|
@@ -2333,9 +2492,17 @@ function addAttrsSubcommands(attrs) {
|
|
|
2333
2492
|
{
|
|
2334
2493
|
description: "List all attributes of an entity",
|
|
2335
2494
|
command: "geonic entities attrs list urn:ngsi-ld:Sensor:001"
|
|
2495
|
+
},
|
|
2496
|
+
{
|
|
2497
|
+
description: "List attributes in table format",
|
|
2498
|
+
command: "geonic entities attrs list urn:ngsi-ld:Sensor:001 --format table"
|
|
2499
|
+
},
|
|
2500
|
+
{
|
|
2501
|
+
description: "List attributes with keyValues output",
|
|
2502
|
+
command: "geonic entities attrs list urn:ngsi-ld:Building:store01 --format json"
|
|
2336
2503
|
}
|
|
2337
2504
|
]);
|
|
2338
|
-
const get = attrs.command("get").description("Get a
|
|
2505
|
+
const get = attrs.command("get").description("Get the value and metadata of a single attribute on an entity").argument("<entityId>", "Entity ID").argument("<attrName>", "Attribute name").action(
|
|
2339
2506
|
withErrorHandler(
|
|
2340
2507
|
async (entityId, attrName, _opts, cmd) => {
|
|
2341
2508
|
const client = createClient(cmd);
|
|
@@ -2349,11 +2516,19 @@ function addAttrsSubcommands(attrs) {
|
|
|
2349
2516
|
);
|
|
2350
2517
|
addExamples(get, [
|
|
2351
2518
|
{
|
|
2352
|
-
description: "Get
|
|
2519
|
+
description: "Get the temperature attribute of a sensor",
|
|
2353
2520
|
command: "geonic entities attrs get urn:ngsi-ld:Sensor:001 temperature"
|
|
2521
|
+
},
|
|
2522
|
+
{
|
|
2523
|
+
description: "Get a Relationship attribute to see what it links to",
|
|
2524
|
+
command: "geonic entities attrs get urn:ngsi-ld:Building:store01 owner"
|
|
2525
|
+
},
|
|
2526
|
+
{
|
|
2527
|
+
description: "Get an attribute in table format",
|
|
2528
|
+
command: "geonic entities attrs get urn:ngsi-ld:Sensor:001 location --format table"
|
|
2354
2529
|
}
|
|
2355
2530
|
]);
|
|
2356
|
-
const add = attrs.command("add").description(
|
|
2531
|
+
const add = attrs.command("add").summary("Add attributes to an entity").description(
|
|
2357
2532
|
'Add attributes to an entity\n\nJSON payload example:\n {"humidity": {"type": "Property", "value": 60}}'
|
|
2358
2533
|
).argument("<entityId>", "Entity ID").argument("[json]", "JSON payload (inline, @file, - for stdin, or omit for interactive/pipe)").action(
|
|
2359
2534
|
withErrorHandler(
|
|
@@ -2382,7 +2557,7 @@ function addAttrsSubcommands(attrs) {
|
|
|
2382
2557
|
command: "cat attrs.json | geonic entities attrs add urn:ngsi-ld:Sensor:001"
|
|
2383
2558
|
}
|
|
2384
2559
|
]);
|
|
2385
|
-
const attrUpdate = attrs.command("update").description(
|
|
2560
|
+
const attrUpdate = attrs.command("update").summary("Update a specific attribute of an entity").description(
|
|
2386
2561
|
'Update a specific attribute of an entity\n\nJSON payload example:\n {"type": "Property", "value": 25}'
|
|
2387
2562
|
).argument("<entityId>", "Entity ID").argument("<attrName>", "Attribute name").argument("[json]", "JSON payload (inline, @file, - for stdin, or omit for interactive/pipe)").action(
|
|
2388
2563
|
withErrorHandler(
|
|
@@ -2407,7 +2582,7 @@ function addAttrsSubcommands(attrs) {
|
|
|
2407
2582
|
command: "geonic entities attrs update urn:ngsi-ld:Sensor:001 temperature @attr.json"
|
|
2408
2583
|
}
|
|
2409
2584
|
]);
|
|
2410
|
-
const del = attrs.command("delete").description("
|
|
2585
|
+
const del = attrs.command("delete").description("Remove an attribute from an entity permanently").argument("<entityId>", "Entity ID").argument("<attrName>", "Attribute name").action(
|
|
2411
2586
|
withErrorHandler(
|
|
2412
2587
|
async (entityId, attrName, _opts, cmd) => {
|
|
2413
2588
|
const client = createClient(cmd);
|
|
@@ -2420,8 +2595,12 @@ function addAttrsSubcommands(attrs) {
|
|
|
2420
2595
|
);
|
|
2421
2596
|
addExamples(del, [
|
|
2422
2597
|
{
|
|
2423
|
-
description: "
|
|
2598
|
+
description: "Remove the temperature attribute from a sensor",
|
|
2424
2599
|
command: "geonic entities attrs delete urn:ngsi-ld:Sensor:001 temperature"
|
|
2600
|
+
},
|
|
2601
|
+
{
|
|
2602
|
+
description: "Remove a deprecated attribute from a building entity",
|
|
2603
|
+
command: "geonic entities attrs delete urn:ngsi-ld:Building:store01 legacyCode"
|
|
2425
2604
|
}
|
|
2426
2605
|
]);
|
|
2427
2606
|
}
|
|
@@ -2547,7 +2726,7 @@ function registerEntitiesCommand(program2) {
|
|
|
2547
2726
|
command: "geonic entities get urn:ngsi-ld:Sensor:001 --key-values"
|
|
2548
2727
|
}
|
|
2549
2728
|
]);
|
|
2550
|
-
const create = entities.command("create").description(
|
|
2729
|
+
const create = entities.command("create").summary("Create a new entity").description(
|
|
2551
2730
|
'Create a new entity\n\nJSON payload example:\n {\n "id": "urn:ngsi-ld:Sensor:001",\n "type": "Sensor",\n "temperature": {"type": "Property", "value": 25}\n }'
|
|
2552
2731
|
).argument("[json]", "JSON payload (inline, @file, - for stdin, or omit for interactive/pipe)").action(
|
|
2553
2732
|
withErrorHandler(async (json, _opts, cmd) => {
|
|
@@ -2579,7 +2758,7 @@ function registerEntitiesCommand(program2) {
|
|
|
2579
2758
|
command: "geonic entities create"
|
|
2580
2759
|
}
|
|
2581
2760
|
]);
|
|
2582
|
-
const update = entities.command("update").description(
|
|
2761
|
+
const update = entities.command("update").summary("Update attributes of an entity (PATCH)").description(
|
|
2583
2762
|
'Update attributes of an entity (PATCH)\n\nJSON payload: only specified attributes are modified.\n e.g. {"temperature": {"type": "Property", "value": 30}}'
|
|
2584
2763
|
).argument("<id>", "Entity ID").argument("[json]", "JSON payload (inline, @file, - for stdin, or omit for interactive/pipe)").action(
|
|
2585
2764
|
withErrorHandler(
|
|
@@ -2608,7 +2787,7 @@ function registerEntitiesCommand(program2) {
|
|
|
2608
2787
|
command: "cat attrs.json | geonic entities update urn:ngsi-ld:Sensor:001"
|
|
2609
2788
|
}
|
|
2610
2789
|
]);
|
|
2611
|
-
const replace = entities.command("replace").description(
|
|
2790
|
+
const replace = entities.command("replace").summary("Replace all attributes of an entity (PUT)").description(
|
|
2612
2791
|
'Replace all attributes of an entity (PUT)\n\nJSON payload: all existing attributes are replaced.\n e.g. {"temperature": {"type": "Property", "value": 20}}'
|
|
2613
2792
|
).argument("<id>", "Entity ID").argument("[json]", "JSON payload (inline, @file, - for stdin, or omit for interactive/pipe)").action(
|
|
2614
2793
|
withErrorHandler(
|
|
@@ -2655,7 +2834,7 @@ function registerEntitiesCommand(program2) {
|
|
|
2655
2834
|
command: "cat entities.json | geonic entities upsert"
|
|
2656
2835
|
}
|
|
2657
2836
|
]);
|
|
2658
|
-
const del = entities.command("delete").description("
|
|
2837
|
+
const del = entities.command("delete").description("Permanently delete an entity and all its attributes").argument("<id>", "Entity ID").action(
|
|
2659
2838
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
2660
2839
|
const client = createClient(cmd);
|
|
2661
2840
|
await client.delete(`/entities/${encodeURIComponent(id)}`);
|
|
@@ -2666,6 +2845,10 @@ function registerEntitiesCommand(program2) {
|
|
|
2666
2845
|
{
|
|
2667
2846
|
description: "Delete an entity by ID",
|
|
2668
2847
|
command: "geonic entities delete urn:ngsi-ld:Sensor:001"
|
|
2848
|
+
},
|
|
2849
|
+
{
|
|
2850
|
+
description: "Delete with explicit service tenant",
|
|
2851
|
+
command: "geonic entities delete urn:ngsi-ld:Sensor:001 --service my-tenant"
|
|
2669
2852
|
}
|
|
2670
2853
|
]);
|
|
2671
2854
|
registerAttrsSubcommand(entities);
|
|
@@ -2674,7 +2857,7 @@ function registerEntitiesCommand(program2) {
|
|
|
2674
2857
|
// src/commands/batch.ts
|
|
2675
2858
|
function registerBatchCommand(program2) {
|
|
2676
2859
|
const batch = program2.command("entityOperations").alias("batch").description("Perform batch operations on entities");
|
|
2677
|
-
const create = batch.command("create [json]").description(
|
|
2860
|
+
const create = batch.command("create [json]").summary("Batch create entities").description(
|
|
2678
2861
|
'Batch create entities\n\nJSON payload: an array of NGSI-LD entities.\n e.g. [{"id": "urn:ngsi-ld:Sensor:001", "type": "Sensor"}, ...]'
|
|
2679
2862
|
).action(
|
|
2680
2863
|
withErrorHandler(async (json, _opts, cmd) => {
|
|
@@ -2699,7 +2882,7 @@ function registerBatchCommand(program2) {
|
|
|
2699
2882
|
command: "cat entities.json | geonic batch create"
|
|
2700
2883
|
}
|
|
2701
2884
|
]);
|
|
2702
|
-
const upsert = batch.command("upsert [json]").description(
|
|
2885
|
+
const upsert = batch.command("upsert [json]").summary("Batch upsert entities").description(
|
|
2703
2886
|
"Batch upsert entities\n\nJSON payload: an array of NGSI-LD entities.\nCreates entities that don't exist, updates those that do."
|
|
2704
2887
|
).action(
|
|
2705
2888
|
withErrorHandler(async (json, _opts, cmd) => {
|
|
@@ -2724,7 +2907,7 @@ function registerBatchCommand(program2) {
|
|
|
2724
2907
|
command: "cat entities.json | geonic batch upsert"
|
|
2725
2908
|
}
|
|
2726
2909
|
]);
|
|
2727
|
-
const update = batch.command("update [json]").description(
|
|
2910
|
+
const update = batch.command("update [json]").summary("Batch update entity attributes").description(
|
|
2728
2911
|
"Batch update entity attributes\n\nJSON payload: an array of NGSI-LD entities with attributes to update.\nEach entity must include id and type; only specified attributes are modified."
|
|
2729
2912
|
).action(
|
|
2730
2913
|
withErrorHandler(async (json, _opts, cmd) => {
|
|
@@ -2745,7 +2928,7 @@ function registerBatchCommand(program2) {
|
|
|
2745
2928
|
command: "cat updates.json | geonic batch update"
|
|
2746
2929
|
}
|
|
2747
2930
|
]);
|
|
2748
|
-
const del = batch.command("delete [json]").description(
|
|
2931
|
+
const del = batch.command("delete [json]").summary("Batch delete entities by ID").description(
|
|
2749
2932
|
'Batch delete entities by ID\n\nJSON payload: an array of entity ID strings.\n e.g. ["urn:ngsi-ld:Sensor:001","urn:ngsi-ld:Sensor:002"]'
|
|
2750
2933
|
).action(
|
|
2751
2934
|
withErrorHandler(async (json, _opts, cmd) => {
|
|
@@ -2770,7 +2953,7 @@ function registerBatchCommand(program2) {
|
|
|
2770
2953
|
command: "cat entity-ids.json | geonic batch delete"
|
|
2771
2954
|
}
|
|
2772
2955
|
]);
|
|
2773
|
-
const query = batch.command("query [json]").description(
|
|
2956
|
+
const query = batch.command("query [json]").summary("Query entities by posting a query payload").description(
|
|
2774
2957
|
'Query entities by posting a query payload\n\nJSON payload example:\n {\n "entities": [{"type": "Sensor"}],\n "attrs": ["temperature"],\n "q": "temperature>30"\n }'
|
|
2775
2958
|
).action(
|
|
2776
2959
|
withErrorHandler(async (json, _opts, cmd) => {
|
|
@@ -2795,7 +2978,7 @@ function registerBatchCommand(program2) {
|
|
|
2795
2978
|
command: "cat query.json | geonic batch query"
|
|
2796
2979
|
}
|
|
2797
2980
|
]);
|
|
2798
|
-
const merge = batch.command("merge [json]").description(
|
|
2981
|
+
const merge = batch.command("merge [json]").summary("Batch merge-patch entities").description(
|
|
2799
2982
|
"Batch merge-patch entities\n\nJSON payload: an array of NGSI-LD entities.\nEach entity must include id and type; attributes are merge-patched."
|
|
2800
2983
|
).action(
|
|
2801
2984
|
withErrorHandler(async (json, _opts, cmd) => {
|
|
@@ -2848,7 +3031,7 @@ function registerSubscriptionsCommand(program2) {
|
|
|
2848
3031
|
command: "geonic subscriptions list --count"
|
|
2849
3032
|
}
|
|
2850
3033
|
]);
|
|
2851
|
-
const get = subscriptions.command("get <id>").description("Get a subscription by ID").action(
|
|
3034
|
+
const get = subscriptions.command("get <id>").description("Get a subscription by ID to inspect its notification config, watched attributes, and status").action(
|
|
2852
3035
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
2853
3036
|
const client = createClient(cmd);
|
|
2854
3037
|
const format = getFormat(cmd);
|
|
@@ -2860,11 +3043,15 @@ function registerSubscriptionsCommand(program2) {
|
|
|
2860
3043
|
);
|
|
2861
3044
|
addExamples(get, [
|
|
2862
3045
|
{
|
|
2863
|
-
description: "Get subscription by ID",
|
|
3046
|
+
description: "Get subscription details by ID",
|
|
2864
3047
|
command: "geonic subscriptions get urn:ngsi-ld:Subscription:001"
|
|
3048
|
+
},
|
|
3049
|
+
{
|
|
3050
|
+
description: "Inspect notification endpoint and status in table format",
|
|
3051
|
+
command: "geonic subscriptions get urn:ngsi-ld:Subscription:001 --format table"
|
|
2865
3052
|
}
|
|
2866
3053
|
]);
|
|
2867
|
-
const create = subscriptions.command("create [json]").description(
|
|
3054
|
+
const create = subscriptions.command("create [json]").summary("Create a subscription").description(
|
|
2868
3055
|
'Create a subscription\n\nJSON payload example:\n {\n "type": "Subscription",\n "entities": [{"type": "Sensor"}],\n "watchedAttributes": ["temperature"],\n "notification": {"endpoint": {"uri": "http://localhost:3000/notify"}}\n }'
|
|
2869
3056
|
).action(
|
|
2870
3057
|
withErrorHandler(async (json, _opts, cmd) => {
|
|
@@ -2894,7 +3081,7 @@ function registerSubscriptionsCommand(program2) {
|
|
|
2894
3081
|
command: "geonic subscriptions create"
|
|
2895
3082
|
}
|
|
2896
3083
|
]);
|
|
2897
|
-
const update = subscriptions.command("update <id> [json]").description(
|
|
3084
|
+
const update = subscriptions.command("update <id> [json]").summary("Update a subscription").description(
|
|
2898
3085
|
'Update a subscription\n\nJSON payload: only specified fields are updated.\n e.g. {"description": "Updated subscription"}'
|
|
2899
3086
|
).action(
|
|
2900
3087
|
withErrorHandler(
|
|
@@ -2925,7 +3112,7 @@ function registerSubscriptionsCommand(program2) {
|
|
|
2925
3112
|
command: "cat sub.json | geonic subscriptions update urn:ngsi-ld:Subscription:001"
|
|
2926
3113
|
}
|
|
2927
3114
|
]);
|
|
2928
|
-
const del = subscriptions.command("delete <id>").description("Delete a subscription").action(
|
|
3115
|
+
const del = subscriptions.command("delete <id>").description("Delete a subscription and stop its notifications").action(
|
|
2929
3116
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
2930
3117
|
const client = createClient(cmd);
|
|
2931
3118
|
await client.delete(
|
|
@@ -2936,8 +3123,12 @@ function registerSubscriptionsCommand(program2) {
|
|
|
2936
3123
|
);
|
|
2937
3124
|
addExamples(del, [
|
|
2938
3125
|
{
|
|
2939
|
-
description: "Delete a subscription",
|
|
3126
|
+
description: "Delete a subscription by ID",
|
|
2940
3127
|
command: "geonic subscriptions delete urn:ngsi-ld:Subscription:001"
|
|
3128
|
+
},
|
|
3129
|
+
{
|
|
3130
|
+
description: "Stop notifications by removing the subscription (using alias)",
|
|
3131
|
+
command: "geonic sub delete urn:ngsi-ld:Subscription:001"
|
|
2941
3132
|
}
|
|
2942
3133
|
]);
|
|
2943
3134
|
}
|
|
@@ -2968,7 +3159,7 @@ function registerRegistrationsCommand(program2) {
|
|
|
2968
3159
|
command: "geonic registrations list --limit 10"
|
|
2969
3160
|
}
|
|
2970
3161
|
]);
|
|
2971
|
-
const get = registrations.command("get <id>").description("Get a registration by ID").action(
|
|
3162
|
+
const get = registrations.command("get <id>").description("Get a registration by ID to inspect its federation endpoint and entity routing").action(
|
|
2972
3163
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
2973
3164
|
const client = createClient(cmd);
|
|
2974
3165
|
const format = getFormat(cmd);
|
|
@@ -2980,11 +3171,15 @@ function registerRegistrationsCommand(program2) {
|
|
|
2980
3171
|
);
|
|
2981
3172
|
addExamples(get, [
|
|
2982
3173
|
{
|
|
2983
|
-
description: "Get registration by ID",
|
|
3174
|
+
description: "Get registration details by ID",
|
|
2984
3175
|
command: "geonic registrations get urn:ngsi-ld:ContextSourceRegistration:001"
|
|
3176
|
+
},
|
|
3177
|
+
{
|
|
3178
|
+
description: "Inspect federation config in table format",
|
|
3179
|
+
command: "geonic registrations get urn:ngsi-ld:ContextSourceRegistration:001 --format table"
|
|
2985
3180
|
}
|
|
2986
3181
|
]);
|
|
2987
|
-
const create = registrations.command("create [json]").description(
|
|
3182
|
+
const create = registrations.command("create [json]").summary("Create a registration").description(
|
|
2988
3183
|
'Create a registration\n\nJSON payload example:\n {\n "type": "ContextSourceRegistration",\n "information": [{"entities": [{"type": "Room"}]}],\n "endpoint": "http://localhost:4000/source"\n }'
|
|
2989
3184
|
).action(
|
|
2990
3185
|
withErrorHandler(async (json, _opts, cmd) => {
|
|
@@ -3039,7 +3234,7 @@ function registerRegistrationsCommand(program2) {
|
|
|
3039
3234
|
command: "cat registration.json | geonic registrations update urn:ngsi-ld:ContextSourceRegistration:001"
|
|
3040
3235
|
}
|
|
3041
3236
|
]);
|
|
3042
|
-
const del = registrations.command("delete <id>").description("Delete a registration").action(
|
|
3237
|
+
const del = registrations.command("delete <id>").description("Delete a registration and remove its forwarding rule").action(
|
|
3043
3238
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3044
3239
|
const client = createClient(cmd);
|
|
3045
3240
|
await client.delete(
|
|
@@ -3050,16 +3245,20 @@ function registerRegistrationsCommand(program2) {
|
|
|
3050
3245
|
);
|
|
3051
3246
|
addExamples(del, [
|
|
3052
3247
|
{
|
|
3053
|
-
description: "Delete a registration",
|
|
3248
|
+
description: "Delete a registration by ID",
|
|
3054
3249
|
command: "geonic registrations delete urn:ngsi-ld:ContextSourceRegistration:001"
|
|
3250
|
+
},
|
|
3251
|
+
{
|
|
3252
|
+
description: "Remove forwarding rule (using alias)",
|
|
3253
|
+
command: "geonic reg delete urn:ngsi-ld:ContextSourceRegistration:001"
|
|
3055
3254
|
}
|
|
3056
3255
|
]);
|
|
3057
3256
|
}
|
|
3058
3257
|
|
|
3059
3258
|
// src/commands/types.ts
|
|
3060
3259
|
function registerTypesCommand(program2) {
|
|
3061
|
-
const types = program2.command("types").description("
|
|
3062
|
-
const list = types.command("list").description("List
|
|
3260
|
+
const types = program2.command("types").description("Discover what entity types exist in the broker and inspect their structure");
|
|
3261
|
+
const list = types.command("list").description("List all entity types currently stored in the broker").action(
|
|
3063
3262
|
withErrorHandler(async (_opts, cmd) => {
|
|
3064
3263
|
const client = createClient(cmd);
|
|
3065
3264
|
const format = getFormat(cmd);
|
|
@@ -3069,11 +3268,15 @@ function registerTypesCommand(program2) {
|
|
|
3069
3268
|
);
|
|
3070
3269
|
addExamples(list, [
|
|
3071
3270
|
{
|
|
3072
|
-
description: "List all entity types",
|
|
3271
|
+
description: "List all entity types in the broker",
|
|
3073
3272
|
command: "geonic types list"
|
|
3273
|
+
},
|
|
3274
|
+
{
|
|
3275
|
+
description: "List entity types in table format for a quick overview",
|
|
3276
|
+
command: "geonic types list --format table"
|
|
3074
3277
|
}
|
|
3075
3278
|
]);
|
|
3076
|
-
const get = types.command("get <typeName>").description("
|
|
3279
|
+
const get = types.command("get <typeName>").description("Show attribute names and types for a given entity type").action(
|
|
3077
3280
|
withErrorHandler(
|
|
3078
3281
|
async (typeName, _opts, cmd) => {
|
|
3079
3282
|
const client = createClient(cmd);
|
|
@@ -3087,8 +3290,16 @@ function registerTypesCommand(program2) {
|
|
|
3087
3290
|
);
|
|
3088
3291
|
addExamples(get, [
|
|
3089
3292
|
{
|
|
3090
|
-
description: "
|
|
3293
|
+
description: "Inspect the Sensor type to see its attributes",
|
|
3091
3294
|
command: "geonic types get Sensor"
|
|
3295
|
+
},
|
|
3296
|
+
{
|
|
3297
|
+
description: "Inspect a Building type in table format",
|
|
3298
|
+
command: "geonic types get Building --format table"
|
|
3299
|
+
},
|
|
3300
|
+
{
|
|
3301
|
+
description: "Inspect a fully-qualified NGSI-LD type",
|
|
3302
|
+
command: "geonic types get https://uri.fiware.org/ns/data-models#AirQualityObserved"
|
|
3092
3303
|
}
|
|
3093
3304
|
]);
|
|
3094
3305
|
}
|
|
@@ -3214,7 +3425,7 @@ function registerTemporalCommand(program2) {
|
|
|
3214
3425
|
command: "geonic temporal entities get urn:ngsi-ld:Sensor:001 --last-n 10"
|
|
3215
3426
|
}
|
|
3216
3427
|
]);
|
|
3217
|
-
const create = entities.command("create [json]").description(
|
|
3428
|
+
const create = entities.command("create [json]").summary("Create a temporal entity").description(
|
|
3218
3429
|
"Create a temporal entity\n\nJSON payload: an NGSI-LD entity with temporal attribute instances.\nEach attribute value is an array of {value, observedAt} objects."
|
|
3219
3430
|
).action(createCreateAction());
|
|
3220
3431
|
addExamples(create, [
|
|
@@ -3231,11 +3442,15 @@ function registerTemporalCommand(program2) {
|
|
|
3231
3442
|
command: "geonic temporal entities create"
|
|
3232
3443
|
}
|
|
3233
3444
|
]);
|
|
3234
|
-
const del = entities.command("delete <id>").description("Delete a temporal entity
|
|
3445
|
+
const del = entities.command("delete <id>").description("Delete a temporal entity and all its historical attribute data").action(createDeleteAction());
|
|
3235
3446
|
addExamples(del, [
|
|
3236
3447
|
{
|
|
3237
3448
|
description: "Delete temporal data for an entity",
|
|
3238
3449
|
command: "geonic temporal entities delete urn:ngsi-ld:Sensor:001"
|
|
3450
|
+
},
|
|
3451
|
+
{
|
|
3452
|
+
description: "Remove all historical records for a specific entity",
|
|
3453
|
+
command: "geonic temporal entities delete urn:ngsi-ld:WeatherStation:tokyo-01"
|
|
3239
3454
|
}
|
|
3240
3455
|
]);
|
|
3241
3456
|
const opsQuery = addQueryOptions(
|
|
@@ -3271,8 +3486,8 @@ function registerTemporalCommand(program2) {
|
|
|
3271
3486
|
|
|
3272
3487
|
// src/commands/snapshots.ts
|
|
3273
3488
|
function registerSnapshotsCommand(program2) {
|
|
3274
|
-
const snapshots = program2.command("snapshots").description("Manage snapshots");
|
|
3275
|
-
const list = snapshots.command("list").description("List snapshots").option("--limit <n>", "Maximum number of snapshots to return", parseInt).option("--offset <n>", "Skip first N snapshots", parseInt).action(
|
|
3489
|
+
const snapshots = program2.command("snapshots").description("Manage point-in-time snapshots of entity data for backup and cloning");
|
|
3490
|
+
const list = snapshots.command("list").description("List all available snapshots with their IDs, timestamps, and status").option("--limit <n>", "Maximum number of snapshots to return", parseInt).option("--offset <n>", "Skip first N snapshots", parseInt).action(
|
|
3276
3491
|
withErrorHandler(async (_opts, cmd) => {
|
|
3277
3492
|
const client = createClient(cmd);
|
|
3278
3493
|
const format = getFormat(cmd);
|
|
@@ -3290,11 +3505,19 @@ function registerSnapshotsCommand(program2) {
|
|
|
3290
3505
|
command: "geonic snapshots list"
|
|
3291
3506
|
},
|
|
3292
3507
|
{
|
|
3293
|
-
description: "List
|
|
3508
|
+
description: "List the 10 most recent snapshots",
|
|
3294
3509
|
command: "geonic snapshots list --limit 10"
|
|
3510
|
+
},
|
|
3511
|
+
{
|
|
3512
|
+
description: "Paginate through snapshots",
|
|
3513
|
+
command: "geonic snapshots list --limit 5 --offset 10"
|
|
3514
|
+
},
|
|
3515
|
+
{
|
|
3516
|
+
description: "List snapshots in table format",
|
|
3517
|
+
command: "geonic snapshots list --format table"
|
|
3295
3518
|
}
|
|
3296
3519
|
]);
|
|
3297
|
-
const get = snapshots.command("get <id>").description("
|
|
3520
|
+
const get = snapshots.command("get <id>").description("Retrieve details of a specific snapshot including its status and metadata").action(
|
|
3298
3521
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3299
3522
|
const client = createClient(cmd);
|
|
3300
3523
|
const format = getFormat(cmd);
|
|
@@ -3306,11 +3529,15 @@ function registerSnapshotsCommand(program2) {
|
|
|
3306
3529
|
);
|
|
3307
3530
|
addExamples(get, [
|
|
3308
3531
|
{
|
|
3309
|
-
description: "Get a
|
|
3310
|
-
command: "geonic snapshots get
|
|
3532
|
+
description: "Get details of a snapshot by its ID",
|
|
3533
|
+
command: "geonic snapshots get abc123"
|
|
3534
|
+
},
|
|
3535
|
+
{
|
|
3536
|
+
description: "Get snapshot details in table format",
|
|
3537
|
+
command: "geonic snapshots get abc123 --format table"
|
|
3311
3538
|
}
|
|
3312
3539
|
]);
|
|
3313
|
-
const create = snapshots.command("create").description("Create a
|
|
3540
|
+
const create = snapshots.command("create").description("Create a point-in-time snapshot of all current entity data").action(
|
|
3314
3541
|
withErrorHandler(async (_opts, cmd) => {
|
|
3315
3542
|
const client = createClient(cmd);
|
|
3316
3543
|
await client.post("/snapshots");
|
|
@@ -3319,11 +3546,15 @@ function registerSnapshotsCommand(program2) {
|
|
|
3319
3546
|
);
|
|
3320
3547
|
addExamples(create, [
|
|
3321
3548
|
{
|
|
3322
|
-
description: "Create a
|
|
3549
|
+
description: "Create a snapshot of the current entity data",
|
|
3323
3550
|
command: "geonic snapshots create"
|
|
3551
|
+
},
|
|
3552
|
+
{
|
|
3553
|
+
description: "Create a snapshot before performing a bulk update",
|
|
3554
|
+
command: "geonic snapshots create && geonic batch upsert @bulk-update.json"
|
|
3324
3555
|
}
|
|
3325
3556
|
]);
|
|
3326
|
-
const del = snapshots.command("delete <id>").description("
|
|
3557
|
+
const del = snapshots.command("delete <id>").description("Permanently delete a snapshot to free storage").action(
|
|
3327
3558
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3328
3559
|
const client = createClient(cmd);
|
|
3329
3560
|
await client.delete(
|
|
@@ -3334,11 +3565,15 @@ function registerSnapshotsCommand(program2) {
|
|
|
3334
3565
|
);
|
|
3335
3566
|
addExamples(del, [
|
|
3336
3567
|
{
|
|
3337
|
-
description: "Delete a snapshot",
|
|
3338
|
-
command: "geonic snapshots delete
|
|
3568
|
+
description: "Delete a snapshot by its ID",
|
|
3569
|
+
command: "geonic snapshots delete abc123"
|
|
3570
|
+
},
|
|
3571
|
+
{
|
|
3572
|
+
description: "Delete an old snapshot to reclaim storage",
|
|
3573
|
+
command: "geonic snapshots delete old-backup-id"
|
|
3339
3574
|
}
|
|
3340
3575
|
]);
|
|
3341
|
-
const clone = snapshots.command("clone <id>").description("Clone a snapshot
|
|
3576
|
+
const clone = snapshots.command("clone <id>").description("Clone a snapshot to create a duplicate for testing or migration").action(
|
|
3342
3577
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3343
3578
|
const client = createClient(cmd);
|
|
3344
3579
|
const format = getFormat(cmd);
|
|
@@ -3354,8 +3589,12 @@ function registerSnapshotsCommand(program2) {
|
|
|
3354
3589
|
);
|
|
3355
3590
|
addExamples(clone, [
|
|
3356
3591
|
{
|
|
3357
|
-
description: "Clone a snapshot",
|
|
3358
|
-
command: "geonic snapshots clone
|
|
3592
|
+
description: "Clone a snapshot for use in a test environment",
|
|
3593
|
+
command: "geonic snapshots clone abc123"
|
|
3594
|
+
},
|
|
3595
|
+
{
|
|
3596
|
+
description: "Clone a production snapshot to a staging profile",
|
|
3597
|
+
command: "geonic snapshots clone abc123 --profile staging"
|
|
3359
3598
|
}
|
|
3360
3599
|
]);
|
|
3361
3600
|
}
|
|
@@ -3363,7 +3602,7 @@ function registerSnapshotsCommand(program2) {
|
|
|
3363
3602
|
// src/commands/admin/tenants.ts
|
|
3364
3603
|
function registerTenantsCommand(parent) {
|
|
3365
3604
|
const tenants = parent.command("tenants").description("Manage tenants");
|
|
3366
|
-
const list = tenants.command("list").description("List all tenants").action(
|
|
3605
|
+
const list = tenants.command("list").description("List all tenants in the system, including their status and configuration").action(
|
|
3367
3606
|
withErrorHandler(async (_opts, cmd) => {
|
|
3368
3607
|
const client = createClient(cmd);
|
|
3369
3608
|
const format = getFormat(cmd);
|
|
@@ -3375,9 +3614,13 @@ function registerTenantsCommand(parent) {
|
|
|
3375
3614
|
{
|
|
3376
3615
|
description: "List all tenants",
|
|
3377
3616
|
command: "geonic admin tenants list"
|
|
3617
|
+
},
|
|
3618
|
+
{
|
|
3619
|
+
description: "List tenants in table format",
|
|
3620
|
+
command: "geonic admin tenants list --format table"
|
|
3378
3621
|
}
|
|
3379
3622
|
]);
|
|
3380
|
-
const get = tenants.command("get <id>").description("Get a tenant
|
|
3623
|
+
const get = tenants.command("get <id>").description("Get a tenant's details \u2014 name, description, status, and creation date").action(
|
|
3381
3624
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3382
3625
|
const client = createClient(cmd);
|
|
3383
3626
|
const format = getFormat(cmd);
|
|
@@ -3390,11 +3633,15 @@ function registerTenantsCommand(parent) {
|
|
|
3390
3633
|
);
|
|
3391
3634
|
addExamples(get, [
|
|
3392
3635
|
{
|
|
3393
|
-
description: "Get
|
|
3636
|
+
description: "Get tenant details by ID",
|
|
3394
3637
|
command: "geonic admin tenants get <tenant-id>"
|
|
3638
|
+
},
|
|
3639
|
+
{
|
|
3640
|
+
description: "Get tenant details in table format",
|
|
3641
|
+
command: "geonic admin tenants get <tenant-id> --format table"
|
|
3395
3642
|
}
|
|
3396
3643
|
]);
|
|
3397
|
-
const create = tenants.command("create [json]").description(
|
|
3644
|
+
const create = tenants.command("create [json]").summary("Create a new tenant").description(
|
|
3398
3645
|
'Create a new tenant\n\nJSON payload example:\n {\n "name": "production",\n "description": "Production environment tenant"\n }'
|
|
3399
3646
|
).action(
|
|
3400
3647
|
withErrorHandler(async (json, _opts, cmd) => {
|
|
@@ -3430,7 +3677,7 @@ function registerTenantsCommand(parent) {
|
|
|
3430
3677
|
command: "geonic admin tenants create"
|
|
3431
3678
|
}
|
|
3432
3679
|
]);
|
|
3433
|
-
const update = tenants.command("update <id> [json]").description(
|
|
3680
|
+
const update = tenants.command("update <id> [json]").summary("Update a tenant").description(
|
|
3434
3681
|
'Update a tenant\n\nJSON payload: only specified fields are updated.\n e.g. {"name": "new-name", "description": "Updated description"}'
|
|
3435
3682
|
).action(
|
|
3436
3683
|
withErrorHandler(
|
|
@@ -3470,7 +3717,7 @@ function registerTenantsCommand(parent) {
|
|
|
3470
3717
|
command: "geonic admin tenants update <tenant-id>"
|
|
3471
3718
|
}
|
|
3472
3719
|
]);
|
|
3473
|
-
const del = tenants.command("delete <id>").description("
|
|
3720
|
+
const del = tenants.command("delete <id>").description("Permanently delete a tenant and all its associated data. This action cannot be undone").action(
|
|
3474
3721
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3475
3722
|
const client = createClient(cmd);
|
|
3476
3723
|
await client.rawRequest(
|
|
@@ -3482,11 +3729,15 @@ function registerTenantsCommand(parent) {
|
|
|
3482
3729
|
);
|
|
3483
3730
|
addExamples(del, [
|
|
3484
3731
|
{
|
|
3485
|
-
description: "Delete a tenant",
|
|
3732
|
+
description: "Delete a tenant by ID",
|
|
3486
3733
|
command: "geonic admin tenants delete <tenant-id>"
|
|
3734
|
+
},
|
|
3735
|
+
{
|
|
3736
|
+
description: "Delete with verbose output to confirm",
|
|
3737
|
+
command: "geonic admin tenants delete <tenant-id> --verbose"
|
|
3487
3738
|
}
|
|
3488
3739
|
]);
|
|
3489
|
-
const activate = tenants.command("activate <id>").description("Activate a tenant").action(
|
|
3740
|
+
const activate = tenants.command("activate <id>").description("Activate a tenant, restoring API access for all its users").action(
|
|
3490
3741
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3491
3742
|
const client = createClient(cmd);
|
|
3492
3743
|
await client.rawRequest(
|
|
@@ -3498,11 +3749,11 @@ function registerTenantsCommand(parent) {
|
|
|
3498
3749
|
);
|
|
3499
3750
|
addExamples(activate, [
|
|
3500
3751
|
{
|
|
3501
|
-
description: "Activate a tenant",
|
|
3752
|
+
description: "Activate a deactivated tenant",
|
|
3502
3753
|
command: "geonic admin tenants activate <tenant-id>"
|
|
3503
3754
|
}
|
|
3504
3755
|
]);
|
|
3505
|
-
const deactivate = tenants.command("deactivate <id>").description("Deactivate a tenant").action(
|
|
3756
|
+
const deactivate = tenants.command("deactivate <id>").description("Deactivate a tenant, blocking API access for all its users until reactivated").action(
|
|
3506
3757
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3507
3758
|
const client = createClient(cmd);
|
|
3508
3759
|
await client.rawRequest(
|
|
@@ -3514,7 +3765,7 @@ function registerTenantsCommand(parent) {
|
|
|
3514
3765
|
);
|
|
3515
3766
|
addExamples(deactivate, [
|
|
3516
3767
|
{
|
|
3517
|
-
description: "Deactivate a tenant",
|
|
3768
|
+
description: "Deactivate a tenant to temporarily suspend access",
|
|
3518
3769
|
command: "geonic admin tenants deactivate <tenant-id>"
|
|
3519
3770
|
}
|
|
3520
3771
|
]);
|
|
@@ -3523,7 +3774,7 @@ function registerTenantsCommand(parent) {
|
|
|
3523
3774
|
// src/commands/admin/users.ts
|
|
3524
3775
|
function registerUsersCommand(parent) {
|
|
3525
3776
|
const users = parent.command("users").description("Manage users");
|
|
3526
|
-
const list = users.command("list").description("List all users").action(
|
|
3777
|
+
const list = users.command("list").description("List all users across tenants, showing email, role, and status").action(
|
|
3527
3778
|
withErrorHandler(async (_opts, cmd) => {
|
|
3528
3779
|
const client = createClient(cmd);
|
|
3529
3780
|
const format = getFormat(cmd);
|
|
@@ -3535,9 +3786,17 @@ function registerUsersCommand(parent) {
|
|
|
3535
3786
|
{
|
|
3536
3787
|
description: "List all users",
|
|
3537
3788
|
command: "geonic admin users list"
|
|
3789
|
+
},
|
|
3790
|
+
{
|
|
3791
|
+
description: "List users in table format",
|
|
3792
|
+
command: "geonic admin users list --format table"
|
|
3793
|
+
},
|
|
3794
|
+
{
|
|
3795
|
+
description: "List users for a specific tenant",
|
|
3796
|
+
command: "geonic admin users list --service <tenant-id>"
|
|
3538
3797
|
}
|
|
3539
3798
|
]);
|
|
3540
|
-
const get = users.command("get <id>").description("Get a user
|
|
3799
|
+
const get = users.command("get <id>").description("Get a user's details \u2014 email, role, tenant, status, and login history").action(
|
|
3541
3800
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3542
3801
|
const client = createClient(cmd);
|
|
3543
3802
|
const format = getFormat(cmd);
|
|
@@ -3550,11 +3809,15 @@ function registerUsersCommand(parent) {
|
|
|
3550
3809
|
);
|
|
3551
3810
|
addExamples(get, [
|
|
3552
3811
|
{
|
|
3553
|
-
description: "
|
|
3812
|
+
description: "Inspect a user's account details",
|
|
3554
3813
|
command: "geonic admin users get <user-id>"
|
|
3814
|
+
},
|
|
3815
|
+
{
|
|
3816
|
+
description: "Get user details in table format",
|
|
3817
|
+
command: "geonic admin users get <user-id> --format table"
|
|
3555
3818
|
}
|
|
3556
3819
|
]);
|
|
3557
|
-
const create = users.command("create [json]").description(
|
|
3820
|
+
const create = users.command("create [json]").summary("Create a new user").description(
|
|
3558
3821
|
'Create a new user\n\nJSON payload example:\n {\n "email": "user@example.com",\n "password": "SecurePassword123!",\n "role": "tenant_admin",\n "tenantId": "<tenant-id>"\n }\n\nRoles: super_admin, tenant_admin, user\ntenantId is required for tenant_admin and user roles.'
|
|
3559
3822
|
).action(
|
|
3560
3823
|
withErrorHandler(async (json, _opts, cmd) => {
|
|
@@ -3586,7 +3849,7 @@ function registerUsersCommand(parent) {
|
|
|
3586
3849
|
command: "cat user.json | geonic admin users create"
|
|
3587
3850
|
}
|
|
3588
3851
|
]);
|
|
3589
|
-
const update = users.command("update <id> [json]").description(
|
|
3852
|
+
const update = users.command("update <id> [json]").summary("Update a user").description(
|
|
3590
3853
|
'Update a user\n\nJSON payload: only specified fields are updated.\n e.g. {"role": "admin"}'
|
|
3591
3854
|
).action(
|
|
3592
3855
|
withErrorHandler(
|
|
@@ -3618,7 +3881,7 @@ function registerUsersCommand(parent) {
|
|
|
3618
3881
|
command: "cat user.json | geonic admin users update <user-id>"
|
|
3619
3882
|
}
|
|
3620
3883
|
]);
|
|
3621
|
-
const del = users.command("delete <id>").description("
|
|
3884
|
+
const del = users.command("delete <id>").description("Permanently delete a user account. This revokes all access and cannot be undone").action(
|
|
3622
3885
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3623
3886
|
const client = createClient(cmd);
|
|
3624
3887
|
await client.rawRequest(
|
|
@@ -3630,11 +3893,15 @@ function registerUsersCommand(parent) {
|
|
|
3630
3893
|
);
|
|
3631
3894
|
addExamples(del, [
|
|
3632
3895
|
{
|
|
3633
|
-
description: "Delete a user",
|
|
3896
|
+
description: "Delete a user by ID",
|
|
3634
3897
|
command: "geonic admin users delete <user-id>"
|
|
3898
|
+
},
|
|
3899
|
+
{
|
|
3900
|
+
description: "Delete with verbose output",
|
|
3901
|
+
command: "geonic admin users delete <user-id> --verbose"
|
|
3635
3902
|
}
|
|
3636
3903
|
]);
|
|
3637
|
-
const activate = users.command("activate <id>").description("Activate a user").action(
|
|
3904
|
+
const activate = users.command("activate <id>").description("Activate a user account, allowing them to log in and access the API").action(
|
|
3638
3905
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3639
3906
|
const client = createClient(cmd);
|
|
3640
3907
|
await client.rawRequest(
|
|
@@ -3646,11 +3913,11 @@ function registerUsersCommand(parent) {
|
|
|
3646
3913
|
);
|
|
3647
3914
|
addExamples(activate, [
|
|
3648
3915
|
{
|
|
3649
|
-
description: "Activate a user",
|
|
3916
|
+
description: "Activate a deactivated user",
|
|
3650
3917
|
command: "geonic admin users activate <user-id>"
|
|
3651
3918
|
}
|
|
3652
3919
|
]);
|
|
3653
|
-
const deactivate = users.command("deactivate <id>").description("Deactivate a user").action(
|
|
3920
|
+
const deactivate = users.command("deactivate <id>").description("Deactivate a user account, preventing login until reactivated").action(
|
|
3654
3921
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3655
3922
|
const client = createClient(cmd);
|
|
3656
3923
|
await client.rawRequest(
|
|
@@ -3662,11 +3929,11 @@ function registerUsersCommand(parent) {
|
|
|
3662
3929
|
);
|
|
3663
3930
|
addExamples(deactivate, [
|
|
3664
3931
|
{
|
|
3665
|
-
description: "Deactivate a user",
|
|
3932
|
+
description: "Deactivate a user to suspend their access",
|
|
3666
3933
|
command: "geonic admin users deactivate <user-id>"
|
|
3667
3934
|
}
|
|
3668
3935
|
]);
|
|
3669
|
-
const unlock = users.command("unlock <id>").description("Unlock a user").action(
|
|
3936
|
+
const unlock = users.command("unlock <id>").description("Unlock a user account that was locked due to repeated failed login attempts").action(
|
|
3670
3937
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3671
3938
|
const client = createClient(cmd);
|
|
3672
3939
|
await client.rawRequest(
|
|
@@ -3686,8 +3953,8 @@ function registerUsersCommand(parent) {
|
|
|
3686
3953
|
|
|
3687
3954
|
// src/commands/admin/policies.ts
|
|
3688
3955
|
function registerPoliciesCommand(parent) {
|
|
3689
|
-
const policies = parent.command("policies").description("Manage policies");
|
|
3690
|
-
const list = policies.command("list").description("List all policies").action(
|
|
3956
|
+
const policies = parent.command("policies").description("Manage XACML access control policies");
|
|
3957
|
+
const list = policies.command("list").description("List all access control policies, showing their status and priority").action(
|
|
3691
3958
|
withErrorHandler(async (_opts, cmd) => {
|
|
3692
3959
|
const client = createClient(cmd);
|
|
3693
3960
|
const format = getFormat(cmd);
|
|
@@ -3699,9 +3966,13 @@ function registerPoliciesCommand(parent) {
|
|
|
3699
3966
|
{
|
|
3700
3967
|
description: "List all policies",
|
|
3701
3968
|
command: "geonic admin policies list"
|
|
3969
|
+
},
|
|
3970
|
+
{
|
|
3971
|
+
description: "List policies in table format for an overview",
|
|
3972
|
+
command: "geonic admin policies list --format table"
|
|
3702
3973
|
}
|
|
3703
3974
|
]);
|
|
3704
|
-
const get = policies.command("get <id>").description("Get a policy
|
|
3975
|
+
const get = policies.command("get <id>").description("Get a policy's full details \u2014 target rules, effect, priority, and status").action(
|
|
3705
3976
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3706
3977
|
const client = createClient(cmd);
|
|
3707
3978
|
const format = getFormat(cmd);
|
|
@@ -3714,11 +3985,11 @@ function registerPoliciesCommand(parent) {
|
|
|
3714
3985
|
);
|
|
3715
3986
|
addExamples(get, [
|
|
3716
3987
|
{
|
|
3717
|
-
description: "
|
|
3988
|
+
description: "Inspect a policy's rules and target configuration",
|
|
3718
3989
|
command: "geonic admin policies get <policy-id>"
|
|
3719
3990
|
}
|
|
3720
3991
|
]);
|
|
3721
|
-
const create = policies.command("create [json]").description(
|
|
3992
|
+
const create = policies.command("create [json]").summary("Create a new policy").description(
|
|
3722
3993
|
'Create a new policy\n\nJSON payload examples:\n\n Allow all entities:\n {\n "description": "Allow all entities",\n "rules": [{"ruleId": "allow-all", "effect": "Permit"}]\n }\n\n Allow GET access to a specific entity type:\n {\n "description": "Allow GET access to Landmark entities",\n "target": {\n "resources": [{"attributeId": "entityType", "matchValue": "Landmark"}],\n "actions": [{"attributeId": "method", "matchValue": "GET"}]\n },\n "rules": [{"ruleId": "permit-get", "effect": "Permit"}]\n }\n\nTarget fields:\n subjects \u2014 attributeId: role, userId, email, tenantId\n resources \u2014 attributeId: path, entityType, entityId, entityOwner, tenantService, servicePath\n actions \u2014 attributeId: method (GET, POST, PATCH, DELETE)\n\nEach element: {attributeId, matchValue, matchFunction?}\n matchFunction: "string-equal" (default) | "string-regexp" | "glob"\n\nPriority: smaller value = higher precedence (e.g. priority 10 overrides user default at 100).\n tenant_admin: minimum priority 10. user self-service (/me/policies): fixed at 100.\n\nDefault role policies (priority 100):\n user \u2192 /v2/** and /ngsi-ld/** all methods Permit; other data APIs GET only\n api_key \u2192 all Deny, anonymous \u2192 all Deny'
|
|
3723
3994
|
).action(
|
|
3724
3995
|
withErrorHandler(async (json, _opts, cmd) => {
|
|
@@ -3758,7 +4029,7 @@ function registerPoliciesCommand(parent) {
|
|
|
3758
4029
|
command: "cat policy.json | geonic admin policies create"
|
|
3759
4030
|
}
|
|
3760
4031
|
]);
|
|
3761
|
-
const update = policies.command("update <id> [json]").description(
|
|
4032
|
+
const update = policies.command("update <id> [json]").summary("Update a policy").description(
|
|
3762
4033
|
'Update a policy\n\nJSON payload: only specified fields are updated.\n e.g. {"description": "Updated policy"}'
|
|
3763
4034
|
).action(
|
|
3764
4035
|
withErrorHandler(
|
|
@@ -3790,7 +4061,7 @@ function registerPoliciesCommand(parent) {
|
|
|
3790
4061
|
command: "cat policy.json | geonic admin policies update <policy-id>"
|
|
3791
4062
|
}
|
|
3792
4063
|
]);
|
|
3793
|
-
const del = policies.command("delete <id>").description("Delete a policy").action(
|
|
4064
|
+
const del = policies.command("delete <id>").description("Delete a policy. Users or API keys referencing this policy will lose the access it granted").action(
|
|
3794
4065
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3795
4066
|
const client = createClient(cmd);
|
|
3796
4067
|
await client.rawRequest(
|
|
@@ -3802,11 +4073,11 @@ function registerPoliciesCommand(parent) {
|
|
|
3802
4073
|
);
|
|
3803
4074
|
addExamples(del, [
|
|
3804
4075
|
{
|
|
3805
|
-
description: "Delete a policy",
|
|
4076
|
+
description: "Delete a policy by ID",
|
|
3806
4077
|
command: "geonic admin policies delete <policy-id>"
|
|
3807
4078
|
}
|
|
3808
4079
|
]);
|
|
3809
|
-
const activate = policies.command("activate <id>").description("Activate a policy").action(
|
|
4080
|
+
const activate = policies.command("activate <id>").description("Activate a policy so its access control rules are enforced").action(
|
|
3810
4081
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3811
4082
|
const client = createClient(cmd);
|
|
3812
4083
|
await client.rawRequest(
|
|
@@ -3818,11 +4089,11 @@ function registerPoliciesCommand(parent) {
|
|
|
3818
4089
|
);
|
|
3819
4090
|
addExamples(activate, [
|
|
3820
4091
|
{
|
|
3821
|
-
description: "
|
|
4092
|
+
description: "Enable a policy to start enforcing its rules",
|
|
3822
4093
|
command: "geonic admin policies activate <policy-id>"
|
|
3823
4094
|
}
|
|
3824
4095
|
]);
|
|
3825
|
-
const deactivate = policies.command("deactivate <id>").description("Deactivate a policy").action(
|
|
4096
|
+
const deactivate = policies.command("deactivate <id>").description("Deactivate a policy, suspending its rules without deleting it").action(
|
|
3826
4097
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3827
4098
|
const client = createClient(cmd);
|
|
3828
4099
|
await client.rawRequest(
|
|
@@ -3834,7 +4105,7 @@ function registerPoliciesCommand(parent) {
|
|
|
3834
4105
|
);
|
|
3835
4106
|
addExamples(deactivate, [
|
|
3836
4107
|
{
|
|
3837
|
-
description: "
|
|
4108
|
+
description: "Temporarily disable a policy without deleting it",
|
|
3838
4109
|
command: "geonic admin policies deactivate <policy-id>"
|
|
3839
4110
|
}
|
|
3840
4111
|
]);
|
|
@@ -3843,7 +4114,7 @@ function registerPoliciesCommand(parent) {
|
|
|
3843
4114
|
// src/commands/admin/oauth-clients.ts
|
|
3844
4115
|
function registerOAuthClientsCommand(parent) {
|
|
3845
4116
|
const oauthClients = parent.command("oauth-clients").description("Manage OAuth clients");
|
|
3846
|
-
const list = oauthClients.command("list").description("List all OAuth clients").action(
|
|
4117
|
+
const list = oauthClients.command("list").description("List all registered OAuth clients and their configurations").action(
|
|
3847
4118
|
withErrorHandler(async (_opts, cmd) => {
|
|
3848
4119
|
const client = createClient(cmd);
|
|
3849
4120
|
const format = getFormat(cmd);
|
|
@@ -3855,9 +4126,13 @@ function registerOAuthClientsCommand(parent) {
|
|
|
3855
4126
|
{
|
|
3856
4127
|
description: "List all OAuth clients",
|
|
3857
4128
|
command: "geonic admin oauth-clients list"
|
|
4129
|
+
},
|
|
4130
|
+
{
|
|
4131
|
+
description: "List OAuth clients in table format",
|
|
4132
|
+
command: "geonic admin oauth-clients list --format table"
|
|
3858
4133
|
}
|
|
3859
4134
|
]);
|
|
3860
|
-
const get = oauthClients.command("get <id>").description("Get an OAuth client
|
|
4135
|
+
const get = oauthClients.command("get <id>").description("Get an OAuth client's details \u2014 name, client ID, policy, and redirect URIs").action(
|
|
3861
4136
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3862
4137
|
const client = createClient(cmd);
|
|
3863
4138
|
const format = getFormat(cmd);
|
|
@@ -3870,11 +4145,11 @@ function registerOAuthClientsCommand(parent) {
|
|
|
3870
4145
|
);
|
|
3871
4146
|
addExamples(get, [
|
|
3872
4147
|
{
|
|
3873
|
-
description: "
|
|
4148
|
+
description: "Inspect an OAuth client's configuration",
|
|
3874
4149
|
command: "geonic admin oauth-clients get <client-id>"
|
|
3875
4150
|
}
|
|
3876
4151
|
]);
|
|
3877
|
-
const create = oauthClients.command("create [json]").description(
|
|
4152
|
+
const create = oauthClients.command("create [json]").summary("Create a new OAuth client").description(
|
|
3878
4153
|
'Create a new OAuth client\n\nJSON payload example:\n {\n "name": "my-app",\n "policyId": "<policy-id>"\n }'
|
|
3879
4154
|
).action(
|
|
3880
4155
|
withErrorHandler(async (json, _opts, cmd) => {
|
|
@@ -3902,7 +4177,7 @@ function registerOAuthClientsCommand(parent) {
|
|
|
3902
4177
|
command: "cat client.json | geonic admin oauth-clients create"
|
|
3903
4178
|
}
|
|
3904
4179
|
]);
|
|
3905
|
-
const update = oauthClients.command("update <id> [json]").description(
|
|
4180
|
+
const update = oauthClients.command("update <id> [json]").summary("Update an OAuth client").description(
|
|
3906
4181
|
'Update an OAuth client\n\nJSON payload: only specified fields are updated.\n e.g. {"description": "Updated client"}'
|
|
3907
4182
|
).action(
|
|
3908
4183
|
withErrorHandler(
|
|
@@ -3934,7 +4209,7 @@ function registerOAuthClientsCommand(parent) {
|
|
|
3934
4209
|
command: "cat client.json | geonic admin oauth-clients update <client-id>"
|
|
3935
4210
|
}
|
|
3936
4211
|
]);
|
|
3937
|
-
const del = oauthClients.command("delete <id>").description("Delete an OAuth client").action(
|
|
4212
|
+
const del = oauthClients.command("delete <id>").description("Delete an OAuth client. Existing tokens issued by this client will be invalidated").action(
|
|
3938
4213
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
3939
4214
|
const client = createClient(cmd);
|
|
3940
4215
|
await client.rawRequest(
|
|
@@ -3946,14 +4221,14 @@ function registerOAuthClientsCommand(parent) {
|
|
|
3946
4221
|
);
|
|
3947
4222
|
addExamples(del, [
|
|
3948
4223
|
{
|
|
3949
|
-
description: "Delete an OAuth client",
|
|
4224
|
+
description: "Delete an OAuth client by ID",
|
|
3950
4225
|
command: "geonic admin oauth-clients delete <client-id>"
|
|
3951
4226
|
}
|
|
3952
4227
|
]);
|
|
3953
4228
|
}
|
|
3954
4229
|
function registerCaddeCommand(parent) {
|
|
3955
|
-
const cadde = parent.command("cadde").description("Manage CADDE configuration");
|
|
3956
|
-
const caddeGet = cadde.command("get").description("Get CADDE configuration").action(
|
|
4230
|
+
const cadde = parent.command("cadde").description("Manage CADDE (data exchange) configuration for cross-platform data sharing");
|
|
4231
|
+
const caddeGet = cadde.command("get").description("Get the current CADDE data exchange configuration (provider, endpoint, etc.)").action(
|
|
3957
4232
|
withErrorHandler(async (_opts, cmd) => {
|
|
3958
4233
|
const client = createClient(cmd);
|
|
3959
4234
|
const format = getFormat(cmd);
|
|
@@ -3963,11 +4238,15 @@ function registerCaddeCommand(parent) {
|
|
|
3963
4238
|
);
|
|
3964
4239
|
addExamples(caddeGet, [
|
|
3965
4240
|
{
|
|
3966
|
-
description: "
|
|
4241
|
+
description: "View current CADDE configuration",
|
|
3967
4242
|
command: "geonic admin cadde get"
|
|
4243
|
+
},
|
|
4244
|
+
{
|
|
4245
|
+
description: "View CADDE configuration in table format",
|
|
4246
|
+
command: "geonic admin cadde get --format table"
|
|
3968
4247
|
}
|
|
3969
4248
|
]);
|
|
3970
|
-
const caddeSet = cadde.command("set [json]").description(
|
|
4249
|
+
const caddeSet = cadde.command("set [json]").summary("Set CADDE configuration").description(
|
|
3971
4250
|
'Set CADDE configuration\n\nJSON payload example:\n {\n "provider": "my-provider",\n "endpoint": "http://localhost:6000"\n }'
|
|
3972
4251
|
).action(
|
|
3973
4252
|
withErrorHandler(async (json, _opts, cmd) => {
|
|
@@ -3995,7 +4274,7 @@ function registerCaddeCommand(parent) {
|
|
|
3995
4274
|
command: "cat cadde-config.json | geonic admin cadde set"
|
|
3996
4275
|
}
|
|
3997
4276
|
]);
|
|
3998
|
-
const caddeDelete = cadde.command("delete").description("
|
|
4277
|
+
const caddeDelete = cadde.command("delete").description("Remove the CADDE data exchange configuration, disabling cross-platform data sharing").action(
|
|
3999
4278
|
withErrorHandler(async (_opts, cmd) => {
|
|
4000
4279
|
const client = createClient(cmd);
|
|
4001
4280
|
await client.rawRequest("DELETE", "/admin/cadde");
|
|
@@ -4004,7 +4283,7 @@ function registerCaddeCommand(parent) {
|
|
|
4004
4283
|
);
|
|
4005
4284
|
addExamples(caddeDelete, [
|
|
4006
4285
|
{
|
|
4007
|
-
description: "
|
|
4286
|
+
description: "Remove CADDE configuration",
|
|
4008
4287
|
command: "geonic admin cadde delete"
|
|
4009
4288
|
}
|
|
4010
4289
|
]);
|
|
@@ -4090,7 +4369,7 @@ function showKeyResult2(data, save, cmd) {
|
|
|
4090
4369
|
}
|
|
4091
4370
|
function registerApiKeysCommand(parent) {
|
|
4092
4371
|
const apiKeys = parent.command("api-keys").description("Manage API keys");
|
|
4093
|
-
const list = apiKeys.command("list").description("List all API keys").option("--tenant-id <id>", "Filter by tenant ID").action(
|
|
4372
|
+
const list = apiKeys.command("list").description("List all API keys, showing name, tenant, policy, and status (key values are masked)").option("--tenant-id <id>", "Filter by tenant ID").action(
|
|
4094
4373
|
withErrorHandler(async (_opts, cmd) => {
|
|
4095
4374
|
const opts = cmd.opts();
|
|
4096
4375
|
const client = createClient(cmd);
|
|
@@ -4110,12 +4389,16 @@ function registerApiKeysCommand(parent) {
|
|
|
4110
4389
|
description: "List all API keys",
|
|
4111
4390
|
command: "geonic admin api-keys list"
|
|
4112
4391
|
},
|
|
4392
|
+
{
|
|
4393
|
+
description: "List API keys in table format",
|
|
4394
|
+
command: "geonic admin api-keys list --format table"
|
|
4395
|
+
},
|
|
4113
4396
|
{
|
|
4114
4397
|
description: "List API keys for a specific tenant",
|
|
4115
4398
|
command: "geonic admin api-keys list --tenant-id <tenant-id>"
|
|
4116
4399
|
}
|
|
4117
4400
|
]);
|
|
4118
|
-
const get = apiKeys.command("get <keyId>").description("Get an API key
|
|
4401
|
+
const get = apiKeys.command("get <keyId>").description("Get an API key's metadata \u2014 name, policy, allowed origins, and rate limit (key value is masked)").action(
|
|
4119
4402
|
withErrorHandler(async (keyId, _opts, cmd) => {
|
|
4120
4403
|
const client = createClient(cmd);
|
|
4121
4404
|
const format = getFormat(cmd);
|
|
@@ -4129,7 +4412,7 @@ function registerApiKeysCommand(parent) {
|
|
|
4129
4412
|
);
|
|
4130
4413
|
addExamples(get, [
|
|
4131
4414
|
{
|
|
4132
|
-
description: "
|
|
4415
|
+
description: "Inspect an API key's configuration",
|
|
4133
4416
|
command: "geonic admin api-keys get <key-id>"
|
|
4134
4417
|
}
|
|
4135
4418
|
]);
|
|
@@ -4256,7 +4539,7 @@ function registerApiKeysCommand(parent) {
|
|
|
4256
4539
|
command: "geonic admin api-keys update <key-id> @key.json"
|
|
4257
4540
|
}
|
|
4258
4541
|
]);
|
|
4259
|
-
const del = apiKeys.command("delete <keyId>").description("Delete an API key").action(
|
|
4542
|
+
const del = apiKeys.command("delete <keyId>").description("Delete an API key. Any requests using this key will be immediately rejected").action(
|
|
4260
4543
|
withErrorHandler(async (keyId, _opts, cmd) => {
|
|
4261
4544
|
const client = createClient(cmd);
|
|
4262
4545
|
await client.rawRequest(
|
|
@@ -4268,7 +4551,7 @@ function registerApiKeysCommand(parent) {
|
|
|
4268
4551
|
);
|
|
4269
4552
|
addExamples(del, [
|
|
4270
4553
|
{
|
|
4271
|
-
description: "Delete an API key",
|
|
4554
|
+
description: "Delete an API key by ID",
|
|
4272
4555
|
command: "geonic admin api-keys delete <key-id>"
|
|
4273
4556
|
}
|
|
4274
4557
|
]);
|
|
@@ -4287,8 +4570,8 @@ function registerAdminCommand(program2) {
|
|
|
4287
4570
|
|
|
4288
4571
|
// src/commands/rules.ts
|
|
4289
4572
|
function registerRulesCommand(program2) {
|
|
4290
|
-
const rules = program2.command("rules").description("Manage
|
|
4291
|
-
const list = rules.command("list").description("List all rules").action(
|
|
4573
|
+
const rules = program2.command("rules").description("Manage ReactiveCore rules that trigger actions based on entity changes");
|
|
4574
|
+
const list = rules.command("list").description("List all configured rules and their current status").action(
|
|
4292
4575
|
withErrorHandler(async (_opts, cmd) => {
|
|
4293
4576
|
const client = createClient(cmd);
|
|
4294
4577
|
const format = getFormat(cmd);
|
|
@@ -4298,11 +4581,15 @@ function registerRulesCommand(program2) {
|
|
|
4298
4581
|
);
|
|
4299
4582
|
addExamples(list, [
|
|
4300
4583
|
{
|
|
4301
|
-
description: "List all rules",
|
|
4584
|
+
description: "List all rules as JSON",
|
|
4302
4585
|
command: "geonic rules list"
|
|
4586
|
+
},
|
|
4587
|
+
{
|
|
4588
|
+
description: "List rules in table format to review status at a glance",
|
|
4589
|
+
command: "geonic rules list --format table"
|
|
4303
4590
|
}
|
|
4304
4591
|
]);
|
|
4305
|
-
const get = rules.command("get <id>").description("Get a rule
|
|
4592
|
+
const get = rules.command("get <id>").description("Get a rule's full definition including conditions, actions, and status").action(
|
|
4306
4593
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
4307
4594
|
const client = createClient(cmd);
|
|
4308
4595
|
const format = getFormat(cmd);
|
|
@@ -4315,11 +4602,15 @@ function registerRulesCommand(program2) {
|
|
|
4315
4602
|
);
|
|
4316
4603
|
addExamples(get, [
|
|
4317
4604
|
{
|
|
4318
|
-
description: "
|
|
4605
|
+
description: "Inspect a rule's conditions and actions",
|
|
4319
4606
|
command: "geonic rules get <rule-id>"
|
|
4607
|
+
},
|
|
4608
|
+
{
|
|
4609
|
+
description: "Get a rule and check if it is active",
|
|
4610
|
+
command: "geonic rules get urn:ngsi-ld:Rule:high-temp-alert"
|
|
4320
4611
|
}
|
|
4321
4612
|
]);
|
|
4322
|
-
const create = rules.command("create [json]").description(
|
|
4613
|
+
const create = rules.command("create [json]").summary("Create a new rule").description(
|
|
4323
4614
|
'Create a new rule\n\nJSON payload example:\n {\n "name": "high-temp-alert",\n "description": "Alert on high temperature",\n "conditions": [{"type": "celExpression", "expression": "entity.temperature > 30"}],\n "actions": [{"type": "webhook", "url": "http://localhost:5000/alert", "method": "POST"}]\n }'
|
|
4324
4615
|
).action(
|
|
4325
4616
|
withErrorHandler(async (json, _opts, cmd) => {
|
|
@@ -4345,7 +4636,7 @@ function registerRulesCommand(program2) {
|
|
|
4345
4636
|
command: "cat rule.json | geonic rules create"
|
|
4346
4637
|
}
|
|
4347
4638
|
]);
|
|
4348
|
-
const update = rules.command("update <id> [json]").description(
|
|
4639
|
+
const update = rules.command("update <id> [json]").summary("Update a rule").description(
|
|
4349
4640
|
'Update a rule\n\nJSON payload: only specified fields are updated.\n e.g. {"description": "Updated rule"}'
|
|
4350
4641
|
).action(
|
|
4351
4642
|
withErrorHandler(
|
|
@@ -4377,7 +4668,7 @@ function registerRulesCommand(program2) {
|
|
|
4377
4668
|
command: "cat rule.json | geonic rules update <rule-id>"
|
|
4378
4669
|
}
|
|
4379
4670
|
]);
|
|
4380
|
-
const del = rules.command("delete <id>").description("
|
|
4671
|
+
const del = rules.command("delete <id>").description("Permanently delete a rule and stop its processing").action(
|
|
4381
4672
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
4382
4673
|
const client = createClient(cmd);
|
|
4383
4674
|
await client.rawRequest(
|
|
@@ -4389,11 +4680,15 @@ function registerRulesCommand(program2) {
|
|
|
4389
4680
|
);
|
|
4390
4681
|
addExamples(del, [
|
|
4391
4682
|
{
|
|
4392
|
-
description: "Delete a rule",
|
|
4683
|
+
description: "Delete a rule by ID",
|
|
4393
4684
|
command: "geonic rules delete <rule-id>"
|
|
4685
|
+
},
|
|
4686
|
+
{
|
|
4687
|
+
description: "Remove an obsolete alert rule",
|
|
4688
|
+
command: "geonic rules delete urn:ngsi-ld:Rule:old-alert"
|
|
4394
4689
|
}
|
|
4395
4690
|
]);
|
|
4396
|
-
const activate = rules.command("activate <id>").description("
|
|
4691
|
+
const activate = rules.command("activate <id>").description("Enable a rule so it begins evaluating conditions and firing actions").action(
|
|
4397
4692
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
4398
4693
|
const client = createClient(cmd);
|
|
4399
4694
|
await client.rawRequest(
|
|
@@ -4405,11 +4700,15 @@ function registerRulesCommand(program2) {
|
|
|
4405
4700
|
);
|
|
4406
4701
|
addExamples(activate, [
|
|
4407
4702
|
{
|
|
4408
|
-
description: "
|
|
4703
|
+
description: "Start processing a rule",
|
|
4409
4704
|
command: "geonic rules activate <rule-id>"
|
|
4705
|
+
},
|
|
4706
|
+
{
|
|
4707
|
+
description: "Re-enable a previously deactivated rule",
|
|
4708
|
+
command: "geonic rules activate urn:ngsi-ld:Rule:high-temp-alert"
|
|
4410
4709
|
}
|
|
4411
4710
|
]);
|
|
4412
|
-
const deactivate = rules.command("deactivate <id>").description("
|
|
4711
|
+
const deactivate = rules.command("deactivate <id>").description("Disable a rule without deleting it, pausing condition evaluation and actions").action(
|
|
4413
4712
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
4414
4713
|
const client = createClient(cmd);
|
|
4415
4714
|
await client.rawRequest(
|
|
@@ -4421,16 +4720,20 @@ function registerRulesCommand(program2) {
|
|
|
4421
4720
|
);
|
|
4422
4721
|
addExamples(deactivate, [
|
|
4423
4722
|
{
|
|
4424
|
-
description: "
|
|
4723
|
+
description: "Temporarily pause a rule during maintenance",
|
|
4425
4724
|
command: "geonic rules deactivate <rule-id>"
|
|
4725
|
+
},
|
|
4726
|
+
{
|
|
4727
|
+
description: "Disable a noisy alert rule without removing it",
|
|
4728
|
+
command: "geonic rules deactivate urn:ngsi-ld:Rule:high-temp-alert"
|
|
4426
4729
|
}
|
|
4427
4730
|
]);
|
|
4428
4731
|
}
|
|
4429
4732
|
|
|
4430
4733
|
// src/commands/models.ts
|
|
4431
4734
|
function registerModelsCommand(program2) {
|
|
4432
|
-
const models = program2.command("custom-data-models").alias("models").description("Manage custom data models");
|
|
4433
|
-
const list = models.command("list").description("List all models").action(
|
|
4735
|
+
const models = program2.command("custom-data-models").alias("models").description("Manage custom data models that define entity type schemas and property constraints");
|
|
4736
|
+
const list = models.command("list").description("List all registered data models for the current tenant").action(
|
|
4434
4737
|
withErrorHandler(async (_opts, cmd) => {
|
|
4435
4738
|
const client = createClient(cmd);
|
|
4436
4739
|
const format = getFormat(cmd);
|
|
@@ -4440,11 +4743,15 @@ function registerModelsCommand(program2) {
|
|
|
4440
4743
|
);
|
|
4441
4744
|
addExamples(list, [
|
|
4442
4745
|
{
|
|
4443
|
-
description: "List all models",
|
|
4746
|
+
description: "List all data models as JSON",
|
|
4444
4747
|
command: "geonic models list"
|
|
4748
|
+
},
|
|
4749
|
+
{
|
|
4750
|
+
description: "Browse available data models in table format",
|
|
4751
|
+
command: "geonic models list --format table"
|
|
4445
4752
|
}
|
|
4446
4753
|
]);
|
|
4447
|
-
const get = models.command("get <id>").description("Get a model
|
|
4754
|
+
const get = models.command("get <id>").description("Get a data model's full schema including property definitions and constraints").action(
|
|
4448
4755
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
4449
4756
|
const client = createClient(cmd);
|
|
4450
4757
|
const format = getFormat(cmd);
|
|
@@ -4457,11 +4764,15 @@ function registerModelsCommand(program2) {
|
|
|
4457
4764
|
);
|
|
4458
4765
|
addExamples(get, [
|
|
4459
4766
|
{
|
|
4460
|
-
description: "
|
|
4767
|
+
description: "Inspect a model's property definitions",
|
|
4461
4768
|
command: "geonic models get <model-id>"
|
|
4769
|
+
},
|
|
4770
|
+
{
|
|
4771
|
+
description: "View the schema for a Sensor data model",
|
|
4772
|
+
command: "geonic models get urn:ngsi-ld:DataModel:Sensor"
|
|
4462
4773
|
}
|
|
4463
4774
|
]);
|
|
4464
|
-
const create = models.command("create [json]").description(
|
|
4775
|
+
const create = models.command("create [json]").summary("Create a new model").description(
|
|
4465
4776
|
'Create a new model\n\nJSON payload example:\n {\n "type": "Sensor",\n "domain": "iot",\n "description": "IoT Sensor",\n "propertyDetails": {\n "temperature": {"ngsiType": "Property", "valueType": "Number", "example": 25}\n }\n }'
|
|
4466
4777
|
).action(
|
|
4467
4778
|
withErrorHandler(async (json, _opts, cmd) => {
|
|
@@ -4487,7 +4798,7 @@ function registerModelsCommand(program2) {
|
|
|
4487
4798
|
command: "cat model.json | geonic models create"
|
|
4488
4799
|
}
|
|
4489
4800
|
]);
|
|
4490
|
-
const update = models.command("update <id> [json]").description(
|
|
4801
|
+
const update = models.command("update <id> [json]").summary("Update a model").description(
|
|
4491
4802
|
'Update a model\n\nJSON payload: only specified fields are updated.\n e.g. {"description": "Updated model"}'
|
|
4492
4803
|
).action(
|
|
4493
4804
|
withErrorHandler(
|
|
@@ -4519,7 +4830,7 @@ function registerModelsCommand(program2) {
|
|
|
4519
4830
|
command: "cat model.json | geonic models update <model-id>"
|
|
4520
4831
|
}
|
|
4521
4832
|
]);
|
|
4522
|
-
const del = models.command("delete <id>").description("Delete a model").action(
|
|
4833
|
+
const del = models.command("delete <id>").description("Delete a data model definition (does not affect existing entities)").action(
|
|
4523
4834
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
4524
4835
|
const client = createClient(cmd);
|
|
4525
4836
|
await client.rawRequest(
|
|
@@ -4531,16 +4842,20 @@ function registerModelsCommand(program2) {
|
|
|
4531
4842
|
);
|
|
4532
4843
|
addExamples(del, [
|
|
4533
4844
|
{
|
|
4534
|
-
description: "Delete a model",
|
|
4845
|
+
description: "Delete a data model by ID",
|
|
4535
4846
|
command: "geonic models delete <model-id>"
|
|
4847
|
+
},
|
|
4848
|
+
{
|
|
4849
|
+
description: "Remove a deprecated model definition",
|
|
4850
|
+
command: "geonic models delete urn:ngsi-ld:DataModel:LegacySensor"
|
|
4536
4851
|
}
|
|
4537
4852
|
]);
|
|
4538
4853
|
}
|
|
4539
4854
|
|
|
4540
4855
|
// src/commands/catalog.ts
|
|
4541
4856
|
function registerCatalogCommand(program2) {
|
|
4542
|
-
const catalog = program2.command("catalog").description("Browse DCAT-AP catalog");
|
|
4543
|
-
const get = catalog.command("get").description("Get the catalog").action(
|
|
4857
|
+
const catalog = program2.command("catalog").description("Browse the DCAT-AP data catalog for discovering and previewing datasets");
|
|
4858
|
+
const get = catalog.command("get").description("Get the DCAT-AP catalog metadata including title, publisher, and dataset count").action(
|
|
4544
4859
|
withErrorHandler(async (_opts, cmd) => {
|
|
4545
4860
|
const client = createClient(cmd);
|
|
4546
4861
|
const format = getFormat(cmd);
|
|
@@ -4550,12 +4865,16 @@ function registerCatalogCommand(program2) {
|
|
|
4550
4865
|
);
|
|
4551
4866
|
addExamples(get, [
|
|
4552
4867
|
{
|
|
4553
|
-
description: "
|
|
4868
|
+
description: "View catalog metadata (title, publisher, datasets summary)",
|
|
4554
4869
|
command: "geonic catalog get"
|
|
4870
|
+
},
|
|
4871
|
+
{
|
|
4872
|
+
description: "Get catalog metadata in table format",
|
|
4873
|
+
command: "geonic catalog get --format table"
|
|
4555
4874
|
}
|
|
4556
4875
|
]);
|
|
4557
|
-
const datasets = catalog.command("datasets").description("
|
|
4558
|
-
const datasetsList = datasets.command("list").description("List all datasets").action(
|
|
4876
|
+
const datasets = catalog.command("datasets").description("List, inspect, and preview datasets published in the catalog");
|
|
4877
|
+
const datasetsList = datasets.command("list").description("List all datasets published in the catalog").action(
|
|
4559
4878
|
withErrorHandler(async (_opts, cmd) => {
|
|
4560
4879
|
const client = createClient(cmd);
|
|
4561
4880
|
const format = getFormat(cmd);
|
|
@@ -4565,11 +4884,15 @@ function registerCatalogCommand(program2) {
|
|
|
4565
4884
|
);
|
|
4566
4885
|
addExamples(datasetsList, [
|
|
4567
4886
|
{
|
|
4568
|
-
description: "List all catalog datasets",
|
|
4887
|
+
description: "List all catalog datasets as JSON",
|
|
4569
4888
|
command: "geonic catalog datasets list"
|
|
4889
|
+
},
|
|
4890
|
+
{
|
|
4891
|
+
description: "Browse datasets in table format",
|
|
4892
|
+
command: "geonic catalog datasets list --format table"
|
|
4570
4893
|
}
|
|
4571
4894
|
]);
|
|
4572
|
-
const datasetsGet = datasets.command("get <id>").description("Get a dataset
|
|
4895
|
+
const datasetsGet = datasets.command("get <id>").description("Get a dataset's metadata including description, distributions, and license").action(
|
|
4573
4896
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
4574
4897
|
const client = createClient(cmd);
|
|
4575
4898
|
const format = getFormat(cmd);
|
|
@@ -4582,11 +4905,15 @@ function registerCatalogCommand(program2) {
|
|
|
4582
4905
|
);
|
|
4583
4906
|
addExamples(datasetsGet, [
|
|
4584
4907
|
{
|
|
4585
|
-
description: "
|
|
4908
|
+
description: "Inspect a dataset's metadata and distributions",
|
|
4586
4909
|
command: "geonic catalog datasets get <dataset-id>"
|
|
4910
|
+
},
|
|
4911
|
+
{
|
|
4912
|
+
description: "View dataset details including license and publisher",
|
|
4913
|
+
command: "geonic catalog datasets get urn:ngsi-ld:Dataset:weather-stations"
|
|
4587
4914
|
}
|
|
4588
4915
|
]);
|
|
4589
|
-
const datasetsSample = datasets.command("sample <id>").description("
|
|
4916
|
+
const datasetsSample = datasets.command("sample <id>").description("Preview sample entities from a dataset to understand its structure and content").action(
|
|
4590
4917
|
withErrorHandler(async (id, _opts, cmd) => {
|
|
4591
4918
|
const client = createClient(cmd);
|
|
4592
4919
|
const format = getFormat(cmd);
|
|
@@ -4599,8 +4926,12 @@ function registerCatalogCommand(program2) {
|
|
|
4599
4926
|
);
|
|
4600
4927
|
addExamples(datasetsSample, [
|
|
4601
4928
|
{
|
|
4602
|
-
description: "
|
|
4929
|
+
description: "Preview sample entities from a dataset",
|
|
4603
4930
|
command: "geonic catalog datasets sample <dataset-id>"
|
|
4931
|
+
},
|
|
4932
|
+
{
|
|
4933
|
+
description: "Preview data in table format to quickly assess content",
|
|
4934
|
+
command: "geonic catalog datasets sample <dataset-id> --format table"
|
|
4604
4935
|
}
|
|
4605
4936
|
]);
|
|
4606
4937
|
}
|
|
@@ -4608,7 +4939,7 @@ function registerCatalogCommand(program2) {
|
|
|
4608
4939
|
// src/commands/health.ts
|
|
4609
4940
|
import { createRequire } from "module";
|
|
4610
4941
|
function registerHealthCommand(program2) {
|
|
4611
|
-
const health = program2.command("health").description("Check
|
|
4942
|
+
const health = program2.command("health").description("Check Context Broker connectivity and health status").action(
|
|
4612
4943
|
withErrorHandler(async (_opts, cmd) => {
|
|
4613
4944
|
const client = createClient(cmd);
|
|
4614
4945
|
const format = getFormat(cmd);
|
|
@@ -4620,11 +4951,19 @@ function registerHealthCommand(program2) {
|
|
|
4620
4951
|
{
|
|
4621
4952
|
description: "Check server health",
|
|
4622
4953
|
command: "geonic health"
|
|
4954
|
+
},
|
|
4955
|
+
{
|
|
4956
|
+
description: "Check health with table output",
|
|
4957
|
+
command: "geonic health --format table"
|
|
4958
|
+
},
|
|
4959
|
+
{
|
|
4960
|
+
description: "Check health of a specific server",
|
|
4961
|
+
command: "geonic health --url https://api.example.com"
|
|
4623
4962
|
}
|
|
4624
4963
|
]);
|
|
4625
4964
|
}
|
|
4626
4965
|
function registerVersionCommand(program2) {
|
|
4627
|
-
const version = program2.command("version").description("Display CLI and server version information").action(
|
|
4966
|
+
const version = program2.command("version").description("Display both CLI and server version information").action(
|
|
4628
4967
|
withErrorHandler(async (_opts, cmd) => {
|
|
4629
4968
|
const require2 = createRequire(import.meta.url);
|
|
4630
4969
|
const pkg = require2("../package.json");
|
|
@@ -4641,11 +4980,16 @@ function registerVersionCommand(program2) {
|
|
|
4641
4980
|
{
|
|
4642
4981
|
description: "Show CLI and server version",
|
|
4643
4982
|
command: "geonic version"
|
|
4983
|
+
},
|
|
4984
|
+
{
|
|
4985
|
+
description: "Show version as JSON",
|
|
4986
|
+
command: "geonic version --format json"
|
|
4644
4987
|
}
|
|
4645
4988
|
]);
|
|
4646
4989
|
}
|
|
4647
4990
|
|
|
4648
4991
|
// src/commands/cli.ts
|
|
4992
|
+
import { execSync } from "child_process";
|
|
4649
4993
|
import { createRequire as createRequire2 } from "module";
|
|
4650
4994
|
function findOption(cmd, program2, flag) {
|
|
4651
4995
|
return cmd.options.find((o) => o.long === flag || o.short === flag) || program2.options.find((o) => o.long === flag || o.short === flag);
|
|
@@ -4811,15 +5155,43 @@ function registerCliCommand(program2) {
|
|
|
4811
5155
|
command: `echo 'eval "$(geonic cli completions zsh)"' >> ~/.zshrc`
|
|
4812
5156
|
}
|
|
4813
5157
|
]);
|
|
4814
|
-
const version = cli.command("version").description("Display the CLI version").action(() => {
|
|
5158
|
+
const version = cli.command("version").description("Display the currently installed CLI version").action(() => {
|
|
4815
5159
|
const require2 = createRequire2(import.meta.url);
|
|
4816
5160
|
const pkg = require2("../package.json");
|
|
4817
5161
|
console.log(pkg.version);
|
|
4818
5162
|
});
|
|
4819
5163
|
addExamples(version, [
|
|
4820
5164
|
{
|
|
4821
|
-
description: "Show
|
|
5165
|
+
description: "Show the installed version",
|
|
4822
5166
|
command: "geonic cli version"
|
|
5167
|
+
},
|
|
5168
|
+
{
|
|
5169
|
+
description: "Use in scripts to check the installed version",
|
|
5170
|
+
command: 'echo "geonic $(geonic cli version)"'
|
|
5171
|
+
}
|
|
5172
|
+
]);
|
|
5173
|
+
const update = cli.command("update").description("Update the CLI to the latest version via npm (requires global install)").action(
|
|
5174
|
+
withErrorHandler(async (...args) => {
|
|
5175
|
+
const cmd = args[args.length - 1];
|
|
5176
|
+
const opts = resolveOptions(cmd);
|
|
5177
|
+
const updateCommand = "npm update -g @geolonia/geonicdb-cli";
|
|
5178
|
+
if (opts.dryRun) {
|
|
5179
|
+
printInfo(`[dry-run] ${updateCommand}`);
|
|
5180
|
+
return;
|
|
5181
|
+
}
|
|
5182
|
+
printInfo("Updating @geolonia/geonicdb-cli...");
|
|
5183
|
+
execSync(updateCommand, { stdio: "inherit" });
|
|
5184
|
+
printSuccess("Update complete.");
|
|
5185
|
+
})
|
|
5186
|
+
);
|
|
5187
|
+
addExamples(update, [
|
|
5188
|
+
{
|
|
5189
|
+
description: "Update CLI to the latest version",
|
|
5190
|
+
command: "geonic cli update"
|
|
5191
|
+
},
|
|
5192
|
+
{
|
|
5193
|
+
description: "Preview the update command without executing it",
|
|
5194
|
+
command: "geonic cli update --dry-run"
|
|
4823
5195
|
}
|
|
4824
5196
|
]);
|
|
4825
5197
|
}
|