mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-14 23:24:52 +09:00
Resolve numeric names in ctl id lookups (#989)
resolveHost, resolveInstance, resolveModPack and retrieveRole treated any integer like argument as an id and never looked it up as a name. An instance created as "10" could not be referenced by name afterwards. The lookup now treats the argument as an id only if an entry with that id exists, and otherwise falls back to a name match. Closes #886 Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
7987a5d925
commit
897c18bd08
2 files changed
+71
-76
No files matched your search
+31
-69
@@ -146,6 +146,22 @@ export class CommandTree {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolve a string with either an id or a name to the id of an entry
|
||||
*
|
||||
* An integer like string is treated as an id if an entry with that id
|
||||
* exists, otherwise it is looked up as a name.
|
||||
*/
|
||||
function resolveId(entries: { id: number, name: string }[], idOrName: string) {
|
||||
if (/^-?\d+$/.test(idOrName)) {
|
||||
const id = parseInt(idOrName, 10);
|
||||
if (entries.some(entry => entry.id === id)) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return entries.find(entry => entry.name === idOrName)?.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a string into a host ID
|
||||
*
|
||||
@@ -157,23 +173,11 @@ export class CommandTree {
|
||||
* @returns host ID.
|
||||
*/
|
||||
export async function resolveHost(client: Link, hostName: string) {
|
||||
let hostId: number | undefined;
|
||||
if (/^-?\d+$/.test(hostName)) {
|
||||
hostId = parseInt(hostName, 10);
|
||||
} else {
|
||||
let hosts = await client.sendTo("controller", new libData.HostListRequest());
|
||||
for (let host of hosts) {
|
||||
if (host.name === hostName) {
|
||||
hostId = host.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hostId === undefined) {
|
||||
throw new libErrors.CommandError(`No host named ${hostName}`);
|
||||
}
|
||||
const hosts = await client.sendTo("controller", new libData.HostListRequest());
|
||||
const hostId = resolveId(hosts, hostName);
|
||||
if (hostId === undefined) {
|
||||
throw new libErrors.CommandError(`No host named ${hostName}`);
|
||||
}
|
||||
|
||||
return hostId;
|
||||
}
|
||||
|
||||
@@ -188,23 +192,11 @@ export async function resolveHost(client: Link, hostName: string) {
|
||||
* @returns instance ID.
|
||||
*/
|
||||
export async function resolveInstance(client: Link, instanceName: string) {
|
||||
let instanceId: number | undefined;
|
||||
if (/^-?\d+$/.test(instanceName)) {
|
||||
instanceId = parseInt(instanceName, 10);
|
||||
} else {
|
||||
let instances = await client.sendTo("controller", new libData.InstanceDetailsListRequest());
|
||||
for (let instance of instances) {
|
||||
if (instance.name === instanceName) {
|
||||
instanceId = instance.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (instanceId === undefined) {
|
||||
throw new libErrors.CommandError(`No instance named ${instanceName}`);
|
||||
}
|
||||
const instances = await client.sendTo("controller", new libData.InstanceDetailsListRequest());
|
||||
const instanceId = resolveId(instances, instanceName);
|
||||
if (instanceId === undefined) {
|
||||
throw new libErrors.CommandError(`No instance named ${instanceName}`);
|
||||
}
|
||||
|
||||
return instanceId;
|
||||
}
|
||||
|
||||
@@ -220,23 +212,11 @@ export async function resolveInstance(client: Link, instanceName: string) {
|
||||
* @returns mod pack ID.
|
||||
*/
|
||||
export async function resolveModPack(client: Link, modPackName: string) {
|
||||
let modPackId: number | undefined;
|
||||
if (/^-?\d+/.test(modPackName)) {
|
||||
modPackId = parseInt(modPackName, 10);
|
||||
} else {
|
||||
let modPacks = await client.sendTo("controller", new libData.ModPackListRequest());
|
||||
for (let modPack of modPacks) {
|
||||
if (modPack.name === modPackName) {
|
||||
modPackId = modPack.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (modPackId === undefined) {
|
||||
throw new libErrors.CommandError(`No mod pack named ${modPackName}`);
|
||||
}
|
||||
const modPacks = await client.sendTo("controller", new libData.ModPackListRequest());
|
||||
const modPackId = resolveId(modPacks, modPackName);
|
||||
if (modPackId === undefined) {
|
||||
throw new libErrors.CommandError(`No mod pack named ${modPackName}`);
|
||||
}
|
||||
|
||||
return modPackId;
|
||||
}
|
||||
|
||||
@@ -251,27 +231,9 @@ export async function resolveModPack(client: Link, modPackName: string) {
|
||||
* @returns Role info.
|
||||
*/
|
||||
export async function retrieveRole(client: Link, roleName: string) {
|
||||
let roles = await client.sendTo("controller", new libData.RoleListRequest());
|
||||
|
||||
let resolvedRole: libData.Role | undefined;
|
||||
if (/^-?\d+$/.test(roleName)) {
|
||||
let roleId = parseInt(roleName, 10);
|
||||
for (let role of roles) {
|
||||
if (role.id === roleId) {
|
||||
resolvedRole = role;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
for (let role of roles) {
|
||||
if (role.name === roleName) {
|
||||
resolvedRole = role;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const roles = await client.sendTo("controller", new libData.RoleListRequest());
|
||||
const roleId = resolveId(roles, roleName);
|
||||
const resolvedRole = roles.find(role => role.id === roleId);
|
||||
if (!resolvedRole) {
|
||||
throw new libErrors.CommandError(`No role named ${roleName}`);
|
||||
}
|
||||
|
||||
+40
-7
@@ -16,39 +16,68 @@ describe("lib/command", function() {
|
||||
let testControl = new mock.MockControl(controlConnector);
|
||||
let testController = new lib.Link(controllerConnector);
|
||||
testController.handle(
|
||||
lib.HostListRequest, () => [new lib.HostDetails("0.1", "Test Host", 11, false)]
|
||||
lib.HostListRequest, () => [
|
||||
new lib.HostDetails("0.1", "Test Host", 11, false),
|
||||
new lib.HostDetails("0.1", "22", 12, false),
|
||||
new lib.HostDetails("0.1", "12", 13, false),
|
||||
]
|
||||
);
|
||||
testController.handle(
|
||||
lib.InstanceDetailsListRequest, () => [new lib.InstanceDetails("Test Instance", 57, 4, undefined, "stopped")]
|
||||
lib.InstanceDetailsListRequest, () => [
|
||||
new lib.InstanceDetails("Test Instance", 57, 4, undefined, "stopped"),
|
||||
new lib.InstanceDetails("10", 58, 4, undefined, "stopped"),
|
||||
new lib.InstanceDetails("58", 59, 4, undefined, "stopped"),
|
||||
]
|
||||
);
|
||||
testController.handle(lib.RoleListRequest, () => [testRole.toJSON()]);
|
||||
let numericRole = lib.Role.fromJSON({ id: 29, name: "77", description: "Test", permissions: [] });
|
||||
testController.handle(lib.RoleListRequest, () => [testRole.toJSON(), numericRole.toJSON()]);
|
||||
|
||||
describe("resolveHost", function() {
|
||||
it("should pass an integer like string back", async function() {
|
||||
assert.equal(await lib.resolveHost(null, "123"), 123);
|
||||
it("should resolve an integer like string as an id", async function() {
|
||||
assert.equal(await lib.resolveHost(testControl, "11"), 11);
|
||||
});
|
||||
it("should resolve a host name with the controller", async function() {
|
||||
assert.equal(await lib.resolveHost(testControl, "Test Host"), 11);
|
||||
});
|
||||
it("should resolve an integer like name if no host has that id", async function() {
|
||||
assert.equal(await lib.resolveHost(testControl, "22"), 12);
|
||||
});
|
||||
it("should prefer the id over an integer like name", async function() {
|
||||
assert.equal(await lib.resolveHost(testControl, "12"), 12);
|
||||
});
|
||||
it("should throw if host is not found", async function() {
|
||||
await assert.rejects(
|
||||
lib.resolveHost(testControl, "invalid"),
|
||||
new lib.CommandError("No host named invalid")
|
||||
);
|
||||
await assert.rejects(
|
||||
lib.resolveHost(testControl, "123"),
|
||||
new lib.CommandError("No host named 123")
|
||||
);
|
||||
});
|
||||
});
|
||||
describe("resolveInstance", function() {
|
||||
it("should pass an integer like string back", async function() {
|
||||
assert.equal(await lib.resolveInstance(null, "123"), 123);
|
||||
it("should resolve an integer like string as an id", async function() {
|
||||
assert.equal(await lib.resolveInstance(testControl, "57"), 57);
|
||||
});
|
||||
it("should resolve an instance name with the controller", async function() {
|
||||
assert.equal(await lib.resolveInstance(testControl, "Test Instance"), 57);
|
||||
});
|
||||
it("should resolve an integer like name if no instance has that id", async function() {
|
||||
assert.equal(await lib.resolveInstance(testControl, "10"), 58);
|
||||
});
|
||||
it("should prefer the id over an integer like name", async function() {
|
||||
assert.equal(await lib.resolveInstance(testControl, "58"), 58);
|
||||
});
|
||||
it("should throw if instance is not found", async function() {
|
||||
await assert.rejects(
|
||||
lib.resolveInstance(testControl, "invalid"),
|
||||
new lib.CommandError("No instance named invalid")
|
||||
);
|
||||
await assert.rejects(
|
||||
lib.resolveInstance(testControl, "123"),
|
||||
new lib.CommandError("No instance named 123")
|
||||
);
|
||||
});
|
||||
});
|
||||
describe("retrieveRole", function() {
|
||||
@@ -58,6 +87,10 @@ describe("lib/command", function() {
|
||||
it("should retrieve a role by name from a string", async function() {
|
||||
assert.deepEqual(await lib.retrieveRole(testControl, "Test Role"), testRole);
|
||||
});
|
||||
it("should retrieve a role by an integer like name if no role has that id", async function() {
|
||||
assert.deepEqual(await lib.retrieveRole(testControl, "28"), testRole);
|
||||
assert.deepEqual(await lib.retrieveRole(testControl, "77"), numericRole);
|
||||
});
|
||||
it("should throw if role is not found", async function() {
|
||||
await assert.rejects(
|
||||
lib.retrieveRole(testControl, "invalid"),
|
||||
|
||||
Reference in new issue
Block a user