refactor(api): extract assistant country read slice
This commit is contained in:
@@ -45,12 +45,13 @@
|
||||
- the audit-history assistant helpers now live in their own domain module, keeping controller-side change-history reads out of the monolithic assistant router without changing the assistant contract
|
||||
- the import/export and staged Dispo assistant helpers now live in their own domain module, keeping file-bound export/import and batch-staging orchestration out of the monolithic assistant router without changing the assistant contract
|
||||
- the remaining estimate search, planning lookup, self-service timeline read, and navigation assistant helpers now live in their own domain module, keeping another mixed read-only cluster out of the monolithic assistant router without changing the assistant contract
|
||||
- the country listing and country detail assistant helpers now live in their own domain module, keeping the remaining geo/readmodel lookups out of the monolithic assistant router without changing the assistant contract
|
||||
|
||||
## Next Up
|
||||
|
||||
Pin the next structural cleanup on the API side:
|
||||
continue splitting `packages/api/src/router/assistant-tools.ts` into domain-oriented tool modules without changing the public tool contract.
|
||||
The next clean slice should stay adjacent to the extracted domains and target one cohesive leftover block such as the remaining country read helpers or other small read-only assistant clusters still living in the monolithic router.
|
||||
The next clean slice should stay adjacent to the extracted domains and target one cohesive leftover block such as the remaining workflow helpers or other small assistant clusters still living in the monolithic router.
|
||||
|
||||
## Remaining Major Themes
|
||||
|
||||
|
||||
@@ -85,6 +85,10 @@ import {
|
||||
countryMetroAdminToolDefinitions,
|
||||
createCountryMetroAdminExecutors,
|
||||
} from "./assistant-tools/country-metro-admin.js";
|
||||
import {
|
||||
countryReadmodelToolDefinitions,
|
||||
createCountryReadmodelExecutors,
|
||||
} from "./assistant-tools/country-readmodels.js";
|
||||
import {
|
||||
createUserSelfServiceExecutors,
|
||||
userSelfServiceToolDefinitions,
|
||||
@@ -431,7 +435,6 @@ const LEGACY_MONOLITHIC_TOOL_ACCESS: Partial<Record<string, ToolAccessRequiremen
|
||||
get_pending_vacation_approvals: { allowedSystemRoles: [...MANAGER_ASSISTANT_ROLES] },
|
||||
get_entitlement_summary: { allowedSystemRoles: [...MANAGER_ASSISTANT_ROLES] },
|
||||
set_entitlement: { allowedSystemRoles: [...MANAGER_ASSISTANT_ROLES] },
|
||||
get_country: { requiresResourceOverview: true },
|
||||
delete_project: { requiredPermissions: [PermissionKey.MANAGE_PROJECTS] },
|
||||
generate_project_cover: { requiredPermissions: [PermissionKey.MANAGE_PROJECTS] },
|
||||
remove_project_cover: { requiredPermissions: [PermissionKey.MANAGE_PROJECTS] },
|
||||
@@ -2161,34 +2164,7 @@ export const TOOL_DEFINITIONS: ToolDef[] = withToolAccess([
|
||||
...clientMutationToolDefinitions,
|
||||
|
||||
// ── ADMIN / CONFIG READ TOOLS ──
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "list_countries",
|
||||
description: "List countries including working hours, schedule rules, active flag, and metro cities.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
includeInactive: { type: "boolean", description: "Include inactive countries. Default: false." },
|
||||
search: { type: "string", description: "Optional country code or name search." },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "get_country",
|
||||
description: "Get one country with schedule rules, active flag, metro cities, and resource count. Accepts ID, code, or name.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
identifier: { type: "string", description: "Country ID, code, or name." },
|
||||
},
|
||||
required: ["identifier"],
|
||||
},
|
||||
},
|
||||
},
|
||||
...countryReadmodelToolDefinitions,
|
||||
...countryMetroAdminToolDefinitions,
|
||||
...configReadmodelToolDefinitions,
|
||||
...userAdminToolDefinitions,
|
||||
@@ -2627,40 +2603,12 @@ const executors = {
|
||||
|
||||
// ── ADMIN / CONFIG ──
|
||||
|
||||
async list_countries(params: { includeInactive?: boolean; search?: string }, ctx: ToolContext) {
|
||||
const caller = createCountryCaller(createScopedCallerContext(ctx));
|
||||
const countries = await caller.list(
|
||||
params.includeInactive
|
||||
? undefined
|
||||
: { isActive: true },
|
||||
);
|
||||
const normalizedSearch = params.search?.trim().toLowerCase();
|
||||
const filteredCountries = normalizedSearch
|
||||
? countries.filter((country) =>
|
||||
country.code.toLowerCase().includes(normalizedSearch)
|
||||
|| country.name.toLowerCase().includes(normalizedSearch))
|
||||
: countries;
|
||||
|
||||
return {
|
||||
count: filteredCountries.length,
|
||||
countries: filteredCountries.map(formatCountry),
|
||||
};
|
||||
},
|
||||
|
||||
async get_country(params: { identifier: string }, ctx: ToolContext) {
|
||||
const caller = createCountryCaller(createScopedCallerContext(ctx));
|
||||
let country;
|
||||
try {
|
||||
country = await caller.getByIdentifier({ identifier: params.identifier });
|
||||
} catch (error) {
|
||||
const mapped = toAssistantCountryNotFoundError(error);
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return formatCountry(country);
|
||||
},
|
||||
...createCountryReadmodelExecutors({
|
||||
createCountryCaller,
|
||||
createScopedCallerContext,
|
||||
formatCountry,
|
||||
toAssistantCountryNotFoundError,
|
||||
}),
|
||||
...createCountryMetroAdminExecutors({
|
||||
createCountryCaller,
|
||||
createScopedCallerContext,
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import type { Prisma } from "@capakraken/db";
|
||||
import type { TRPCContext } from "../../trpc.js";
|
||||
import { withToolAccess, type ToolContext, type ToolDef, type ToolExecutor } from "./shared.js";
|
||||
|
||||
type AssistantToolErrorResult = { error: string };
|
||||
|
||||
type CountryRecord = {
|
||||
id: string;
|
||||
code: string;
|
||||
name: string;
|
||||
dailyWorkingHours: number;
|
||||
scheduleRules?: Prisma.JsonValue | null;
|
||||
isActive?: boolean | null;
|
||||
metroCities?: Array<{ id: string; name: string }> | null;
|
||||
_count?: { resources?: number | null } | null;
|
||||
};
|
||||
|
||||
type CountryReadmodelsDeps = {
|
||||
createCountryCaller: (ctx: TRPCContext) => {
|
||||
list: (params?: { isActive: boolean }) => Promise<CountryRecord[]>;
|
||||
getByIdentifier: (params: { identifier: string }) => Promise<CountryRecord>;
|
||||
};
|
||||
createScopedCallerContext: (ctx: ToolContext) => TRPCContext;
|
||||
formatCountry: (country: CountryRecord) => unknown;
|
||||
toAssistantCountryNotFoundError: (
|
||||
error: unknown,
|
||||
) => AssistantToolErrorResult | null;
|
||||
};
|
||||
|
||||
export const countryReadmodelToolDefinitions: ToolDef[] = withToolAccess([
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "list_countries",
|
||||
description: "List countries including working hours, schedule rules, active flag, and metro cities.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
includeInactive: { type: "boolean", description: "Include inactive countries. Default: false." },
|
||||
search: { type: "string", description: "Optional country code or name search." },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "get_country",
|
||||
description: "Get one country with schedule rules, active flag, metro cities, and resource count. Accepts ID, code, or name.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
identifier: { type: "string", description: "Country ID, code, or name." },
|
||||
},
|
||||
required: ["identifier"],
|
||||
},
|
||||
},
|
||||
},
|
||||
], {
|
||||
get_country: {
|
||||
requiresResourceOverview: true,
|
||||
},
|
||||
});
|
||||
|
||||
export function createCountryReadmodelExecutors(
|
||||
deps: CountryReadmodelsDeps,
|
||||
): Record<string, ToolExecutor> {
|
||||
return {
|
||||
async list_countries(
|
||||
params: { includeInactive?: boolean; search?: string },
|
||||
ctx: ToolContext,
|
||||
) {
|
||||
const caller = deps.createCountryCaller(deps.createScopedCallerContext(ctx));
|
||||
const countries = await caller.list(
|
||||
params.includeInactive
|
||||
? undefined
|
||||
: { isActive: true },
|
||||
);
|
||||
const normalizedSearch = params.search?.trim().toLowerCase();
|
||||
const filteredCountries = normalizedSearch
|
||||
? countries.filter((country) =>
|
||||
country.code.toLowerCase().includes(normalizedSearch)
|
||||
|| country.name.toLowerCase().includes(normalizedSearch))
|
||||
: countries;
|
||||
|
||||
return {
|
||||
count: filteredCountries.length,
|
||||
countries: filteredCountries.map(deps.formatCountry),
|
||||
};
|
||||
},
|
||||
|
||||
async get_country(
|
||||
params: { identifier: string },
|
||||
ctx: ToolContext,
|
||||
) {
|
||||
const caller = deps.createCountryCaller(deps.createScopedCallerContext(ctx));
|
||||
let country;
|
||||
try {
|
||||
country = await caller.getByIdentifier({ identifier: params.identifier });
|
||||
} catch (error) {
|
||||
const mapped = deps.toAssistantCountryNotFoundError(error);
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
return deps.formatCountry(country);
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user