mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 08:27:39 +00:00
refactor: unify extension allowlist resolver and directory scaffolding
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
listDirectoryGroupEntriesFromMapKeysAndAllowFrom,
|
||||
listDirectoryGroupEntriesFromMapKeys,
|
||||
listDirectoryUserEntriesFromAllowFromAndMapKeys,
|
||||
listDirectoryUserEntriesFromAllowFrom,
|
||||
} from "./directory-config-helpers.js";
|
||||
|
||||
@@ -37,3 +39,41 @@ describe("listDirectoryGroupEntriesFromMapKeys", () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("listDirectoryUserEntriesFromAllowFromAndMapKeys", () => {
|
||||
it("merges allowFrom and map keys with dedupe/query/limit", () => {
|
||||
const entries = listDirectoryUserEntriesFromAllowFromAndMapKeys({
|
||||
allowFrom: ["user:alice", "user:bob"],
|
||||
map: {
|
||||
"user:carla": {},
|
||||
"user:alice": {},
|
||||
},
|
||||
normalizeAllowFromId: (entry) => entry.replace(/^user:/i, ""),
|
||||
normalizeMapKeyId: (entry) => entry.replace(/^user:/i, ""),
|
||||
query: "a",
|
||||
limit: 2,
|
||||
});
|
||||
|
||||
expect(entries).toEqual([
|
||||
{ kind: "user", id: "alice" },
|
||||
{ kind: "user", id: "carla" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("listDirectoryGroupEntriesFromMapKeysAndAllowFrom", () => {
|
||||
it("merges groups keys and group allowFrom entries", () => {
|
||||
const entries = listDirectoryGroupEntriesFromMapKeysAndAllowFrom({
|
||||
groups: {
|
||||
"team/a": {},
|
||||
},
|
||||
allowFrom: ["team/b", "team/a"],
|
||||
query: "team/",
|
||||
});
|
||||
|
||||
expect(entries).toEqual([
|
||||
{ kind: "group", id: "team/a" },
|
||||
{ kind: "group", id: "team/b" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,44 +22,106 @@ function toDirectoryEntries(kind: "user" | "group", ids: string[]): ChannelDirec
|
||||
return ids.map((id) => ({ kind, id }) as const);
|
||||
}
|
||||
|
||||
function collectDirectoryIdsFromEntries(params: {
|
||||
entries?: readonly unknown[];
|
||||
normalizeId?: (entry: string) => string | null | undefined;
|
||||
}): string[] {
|
||||
return (params.entries ?? [])
|
||||
.map((entry) => String(entry).trim())
|
||||
.filter((entry) => Boolean(entry) && entry !== "*")
|
||||
.map((entry) => {
|
||||
const normalized = params.normalizeId ? params.normalizeId(entry) : entry;
|
||||
return typeof normalized === "string" ? normalized.trim() : "";
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function collectDirectoryIdsFromMapKeys(params: {
|
||||
groups?: Record<string, unknown>;
|
||||
normalizeId?: (entry: string) => string | null | undefined;
|
||||
}): string[] {
|
||||
return Object.keys(params.groups ?? {})
|
||||
.map((entry) => entry.trim())
|
||||
.filter((entry) => Boolean(entry) && entry !== "*")
|
||||
.map((entry) => {
|
||||
const normalized = params.normalizeId ? params.normalizeId(entry) : entry;
|
||||
return typeof normalized === "string" ? normalized.trim() : "";
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function dedupeDirectoryIds(ids: string[]): string[] {
|
||||
return Array.from(new Set(ids));
|
||||
}
|
||||
|
||||
export function listDirectoryUserEntriesFromAllowFrom(params: {
|
||||
allowFrom?: readonly unknown[];
|
||||
query?: string | null;
|
||||
limit?: number | null;
|
||||
normalizeId?: (entry: string) => string | null | undefined;
|
||||
}): ChannelDirectoryEntry[] {
|
||||
const ids = Array.from(
|
||||
new Set(
|
||||
(params.allowFrom ?? [])
|
||||
.map((entry) => String(entry).trim())
|
||||
.filter((entry) => Boolean(entry) && entry !== "*")
|
||||
.map((entry) => {
|
||||
const normalized = params.normalizeId ? params.normalizeId(entry) : entry;
|
||||
return typeof normalized === "string" ? normalized.trim() : "";
|
||||
})
|
||||
.filter(Boolean),
|
||||
),
|
||||
const ids = dedupeDirectoryIds(
|
||||
collectDirectoryIdsFromEntries({
|
||||
entries: params.allowFrom,
|
||||
normalizeId: params.normalizeId,
|
||||
}),
|
||||
);
|
||||
return toDirectoryEntries("user", applyDirectoryQueryAndLimit(ids, params));
|
||||
}
|
||||
|
||||
export function listDirectoryUserEntriesFromAllowFromAndMapKeys(params: {
|
||||
allowFrom?: readonly unknown[];
|
||||
map?: Record<string, unknown>;
|
||||
query?: string | null;
|
||||
limit?: number | null;
|
||||
normalizeAllowFromId?: (entry: string) => string | null | undefined;
|
||||
normalizeMapKeyId?: (entry: string) => string | null | undefined;
|
||||
}): ChannelDirectoryEntry[] {
|
||||
const ids = dedupeDirectoryIds([
|
||||
...collectDirectoryIdsFromEntries({
|
||||
entries: params.allowFrom,
|
||||
normalizeId: params.normalizeAllowFromId,
|
||||
}),
|
||||
...collectDirectoryIdsFromMapKeys({
|
||||
groups: params.map,
|
||||
normalizeId: params.normalizeMapKeyId,
|
||||
}),
|
||||
]);
|
||||
return toDirectoryEntries("user", applyDirectoryQueryAndLimit(ids, params));
|
||||
}
|
||||
|
||||
export function listDirectoryGroupEntriesFromMapKeys(params: {
|
||||
groups?: Record<string, unknown>;
|
||||
query?: string | null;
|
||||
limit?: number | null;
|
||||
normalizeId?: (entry: string) => string | null | undefined;
|
||||
}): ChannelDirectoryEntry[] {
|
||||
const ids = Array.from(
|
||||
new Set(
|
||||
Object.keys(params.groups ?? {})
|
||||
.map((entry) => entry.trim())
|
||||
.filter((entry) => Boolean(entry) && entry !== "*")
|
||||
.map((entry) => {
|
||||
const normalized = params.normalizeId ? params.normalizeId(entry) : entry;
|
||||
return typeof normalized === "string" ? normalized.trim() : "";
|
||||
})
|
||||
.filter(Boolean),
|
||||
),
|
||||
const ids = dedupeDirectoryIds(
|
||||
collectDirectoryIdsFromMapKeys({
|
||||
groups: params.groups,
|
||||
normalizeId: params.normalizeId,
|
||||
}),
|
||||
);
|
||||
return toDirectoryEntries("group", applyDirectoryQueryAndLimit(ids, params));
|
||||
}
|
||||
|
||||
export function listDirectoryGroupEntriesFromMapKeysAndAllowFrom(params: {
|
||||
groups?: Record<string, unknown>;
|
||||
allowFrom?: readonly unknown[];
|
||||
query?: string | null;
|
||||
limit?: number | null;
|
||||
normalizeMapKeyId?: (entry: string) => string | null | undefined;
|
||||
normalizeAllowFromId?: (entry: string) => string | null | undefined;
|
||||
}): ChannelDirectoryEntry[] {
|
||||
const ids = dedupeDirectoryIds([
|
||||
...collectDirectoryIdsFromMapKeys({
|
||||
groups: params.groups,
|
||||
normalizeId: params.normalizeMapKeyId,
|
||||
}),
|
||||
...collectDirectoryIdsFromEntries({
|
||||
entries: params.allowFrom,
|
||||
normalizeId: params.normalizeAllowFromId,
|
||||
}),
|
||||
]);
|
||||
return toDirectoryEntries("group", applyDirectoryQueryAndLimit(ids, params));
|
||||
}
|
||||
|
||||
@@ -1,40 +1,18 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
mapBasicAllowlistResolutionEntries,
|
||||
type BasicAllowlistResolutionEntry,
|
||||
} from "./allowlist-resolution.js";
|
||||
import { mapAllowlistResolutionInputs } from "./allowlist-resolution.js";
|
||||
|
||||
describe("mapBasicAllowlistResolutionEntries", () => {
|
||||
it("maps entries to normalized allowlist resolver output", () => {
|
||||
const entries: BasicAllowlistResolutionEntry[] = [
|
||||
{
|
||||
input: "alice",
|
||||
resolved: true,
|
||||
id: "U123",
|
||||
name: "Alice",
|
||||
note: "ok",
|
||||
describe("mapAllowlistResolutionInputs", () => {
|
||||
it("maps inputs sequentially and preserves order", async () => {
|
||||
const visited: string[] = [];
|
||||
const result = await mapAllowlistResolutionInputs({
|
||||
inputs: ["one", "two", "three"],
|
||||
mapInput: async (input) => {
|
||||
visited.push(input);
|
||||
return input.toUpperCase();
|
||||
},
|
||||
{
|
||||
input: "bob",
|
||||
resolved: false,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
expect(mapBasicAllowlistResolutionEntries(entries)).toEqual([
|
||||
{
|
||||
input: "alice",
|
||||
resolved: true,
|
||||
id: "U123",
|
||||
name: "Alice",
|
||||
note: "ok",
|
||||
},
|
||||
{
|
||||
input: "bob",
|
||||
resolved: false,
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
note: undefined,
|
||||
},
|
||||
]);
|
||||
expect(visited).toEqual(["one", "two", "three"]);
|
||||
expect(result).toEqual(["ONE", "TWO", "THREE"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,3 +17,14 @@ export function mapBasicAllowlistResolutionEntries(
|
||||
note: entry.note,
|
||||
}));
|
||||
}
|
||||
|
||||
export async function mapAllowlistResolutionInputs<T>(params: {
|
||||
inputs: string[];
|
||||
mapInput: (input: string) => Promise<T> | T;
|
||||
}): Promise<T[]> {
|
||||
const results: T[] = [];
|
||||
for (const input of params.inputs) {
|
||||
results.push(await params.mapInput(input));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -133,6 +133,7 @@ export { isDangerousNameMatchingEnabled } from "../config/dangerous-name-matchin
|
||||
export type { FileLockHandle, FileLockOptions } from "./file-lock.js";
|
||||
export { acquireFileLock, withFileLock } from "./file-lock.js";
|
||||
export {
|
||||
mapAllowlistResolutionInputs,
|
||||
mapBasicAllowlistResolutionEntries,
|
||||
type BasicAllowlistResolutionEntry,
|
||||
} from "./allowlist-resolution.js";
|
||||
@@ -515,6 +516,12 @@ export { optionalStringEnum, stringEnum } from "../agents/schema/typebox.js";
|
||||
export type { PollInput } from "../polls.js";
|
||||
|
||||
export { buildChannelConfigSchema } from "../channels/plugins/config-schema.js";
|
||||
export {
|
||||
listDirectoryGroupEntriesFromMapKeys,
|
||||
listDirectoryGroupEntriesFromMapKeysAndAllowFrom,
|
||||
listDirectoryUserEntriesFromAllowFrom,
|
||||
listDirectoryUserEntriesFromAllowFromAndMapKeys,
|
||||
} from "../channels/plugins/directory-config-helpers.js";
|
||||
export {
|
||||
clearAccountEntryFields,
|
||||
deleteAccountFromConfigSection,
|
||||
|
||||
Reference in New Issue
Block a user