import { tryResolveConfiguration } from "@dh-software/baukasten-config-reader";
|
import {
|
GLOBAL_CUSTOMER_NUMBER,
|
type ConfigurationEntry,
|
type CustomerNumber,
|
type PermissionOptions,
|
type ResolvedPermission,
|
} from "@dh-software/baukasten-types";
|
import { listCustomerNumbers, readFileInfo } from "./fileinfo";
|
|
/**
|
* Resolves the permission for a single key of the global 14243 folder by reusing
|
* the config-reader's locator (the public/private rule lives there — single
|
* source of truth). Returns null and warns when the key is listed in
|
* fileinfo.json but no backing file exists.
|
*/
|
async function resolveGlobalPermission(
|
baseDir: string,
|
key: string,
|
): Promise<ResolvedPermission | null> {
|
const resolved = await tryResolveConfiguration(baseDir, {
|
customerNumber: GLOBAL_CUSTOMER_NUMBER,
|
key,
|
});
|
if (resolved === null) {
|
console.warn(
|
`[baukasten] Configuration "${key}" is listed in the fileinfo.json of customer ` +
|
`${GLOBAL_CUSTOMER_NUMBER} but no matching file was found in public/ or private/; ` +
|
`skipping.`,
|
);
|
return null;
|
}
|
// For the global folder, location is always "public" or "private".
|
return {
|
owner: GLOBAL_CUSTOMER_NUMBER,
|
consumers: [],
|
blanket: resolved.location === "public" ? "public" : "private",
|
};
|
}
|
|
/** Builds the catalog entries for one customer folder. */
|
export async function readCustomerCatalog(
|
baseDir: string,
|
customerNumber: CustomerNumber,
|
): Promise<ConfigurationEntry[]> {
|
const fileInfo = await readFileInfo(baseDir, customerNumber);
|
const entries: ConfigurationEntry[] = [];
|
|
for (const [key, info] of Object.entries(fileInfo)) {
|
let permission: ResolvedPermission | null;
|
|
if (customerNumber === GLOBAL_CUSTOMER_NUMBER) {
|
permission = await resolveGlobalPermission(baseDir, key);
|
if (permission === null) continue;
|
} else {
|
const accessPermission = info["access-permission"];
|
permission = {
|
owner: customerNumber,
|
consumers: accessPermission?.consumers ?? [],
|
blanket: accessPermission?.["blanket-permission"] ?? "self",
|
};
|
}
|
|
entries.push({
|
customerNumber,
|
key,
|
displayName: info["display-name"],
|
permission,
|
});
|
}
|
|
return entries;
|
}
|
|
/** Builds the full catalog across all customer folders under `baseDir`. */
|
export async function readCatalog(baseDir: string): Promise<ConfigurationEntry[]> {
|
const customerNumbers = await listCustomerNumbers(baseDir);
|
const catalogs = await Promise.all(
|
customerNumbers.map((customerNumber) => readCustomerCatalog(baseDir, customerNumber)),
|
);
|
return catalogs.flat();
|
}
|
|
/** Decides whether `requestingCustomerNumber` may select the given configuration. */
|
export function canSelect(
|
entry: ConfigurationEntry,
|
requestingCustomerNumber: CustomerNumber,
|
options: PermissionOptions = {},
|
): boolean {
|
const { owner, consumers, blanket } = entry.permission;
|
|
if (requestingCustomerNumber === owner) return true;
|
|
switch (blanket) {
|
case "public":
|
return true;
|
case "private":
|
case "self":
|
// Beyond the owner (handled above), only explicit consumers.
|
return consumers.includes(requestingCustomerNumber);
|
case "stores":
|
if (consumers.includes(requestingCustomerNumber)) return true;
|
return options.isStoreMember?.(owner, requestingCustomerNumber) ?? false;
|
}
|
|
return false;
|
}
|
|
/** Filters a catalog to the entries `requestingCustomerNumber` may select. */
|
export function selectableFor(
|
catalog: ConfigurationEntry[],
|
requestingCustomerNumber: CustomerNumber,
|
options: PermissionOptions = {},
|
): ConfigurationEntry[] {
|
return catalog.filter((entry) => canSelect(entry, requestingCustomerNumber, options));
|
}
|
|
/** Convenience: read the catalog and filter it for one customer in a single call. */
|
export async function getSelectableConfigurations(
|
baseDir: string,
|
requestingCustomerNumber: CustomerNumber,
|
options: PermissionOptions = {},
|
): Promise<ConfigurationEntry[]> {
|
const catalog = await readCatalog(baseDir);
|
return selectableFor(catalog, requestingCustomerNumber, options);
|
}
|