import { z } from 'zod';
import { makeAuthenticatedRequest, createToolResponse } from '../../utils/apiHelpers.js';
import { formatSuccess } from '../../utils/responseHelpers.js';
import { REALMS } from '../../config/managedObjectUtils.js';
const aicBaseUrl = process.env.AIC_BASE_URL;
const SCOPES = ['fr:idm:*'];
export const updateThemeTool = {
name: 'updateTheme',
title: 'Update Theme',
description: 'Update an existing theme in PingOne AIC',
scopes: SCOPES,
annotations: {
idempotentHint: true,
openWorldHint: true
},
inputSchema: {
realm: z.enum(REALMS).describe('Realm name'),
themeIdentifier: z.string().describe('Theme ID or name'),
themeUpdates: z.record(z.any()).describe('Object containing the fields to update (cannot update _id or isDefault)')
},
async toolFunction({ realm, themeIdentifier, themeUpdates }: { realm: string; themeIdentifier: string; themeUpdates: Record<string, any> }) {
try {
// Validate that themeUpdates don't contain protected fields
if ('_id' in themeUpdates) {
return createToolResponse('Cannot update the "_id" field. Theme IDs are immutable.');
}
if ('isDefault' in themeUpdates) {
return createToolResponse('Cannot update "isDefault" directly. Use the setDefaultTheme tool to change the default theme.');
}
// Get the current theme configuration
const configUrl = `https://${aicBaseUrl}/openidm/config/ui/themerealm`;
const { data: config } = await makeAuthenticatedRequest(configUrl, SCOPES);
// Validate config structure
if (!config || !(config as any).realm || !(config as any).realm[realm]) {
return createToolResponse(`Invalid theme configuration structure for realm "${realm}"`);
}
const realmThemes = (config as any).realm[realm];
// Find the theme by ID or name
const themeIndex = realmThemes.findIndex((t: any) =>
t._id === themeIdentifier || t.name === themeIdentifier
);
if (themeIndex === -1) {
return createToolResponse(`Theme not found: "${themeIdentifier}" in realm "${realm}"`);
}
const existingTheme = realmThemes[themeIndex];
const themeId = existingTheme._id;
const originalName = existingTheme.name;
// If updating the name, check for duplicates
if (themeUpdates.name && themeUpdates.name !== originalName) {
const duplicateTheme = realmThemes.find((t: any) => t.name === themeUpdates.name && t._id !== themeId);
if (duplicateTheme) {
return createToolResponse(`Theme with name "${themeUpdates.name}" already exists in realm "${realm}". Choose a different name.`);
}
}
// Merge themeUpdates with existing theme, preserving _id and isDefault
const updatedTheme = {
...existingTheme,
...themeUpdates,
_id: themeId, // Always preserve the ID
isDefault: existingTheme.isDefault // Always preserve isDefault
};
// Update the themes array
const updatedThemes = [...realmThemes];
updatedThemes[themeIndex] = updatedTheme;
// Update the config
const updatedConfig = {
...config,
realm: {
...(config as any).realm,
[realm]: updatedThemes
}
};
// PUT the updated configuration back
const { response } = await makeAuthenticatedRequest(
configUrl,
SCOPES,
{
method: 'PUT',
body: JSON.stringify(updatedConfig)
}
);
const themeName = updatedTheme.name;
const successMessage = `Updated theme "${themeName}" (${themeId}) in realm "${realm}"`;
return createToolResponse(formatSuccess({ _id: themeId, name: themeName, message: successMessage }, response));
} catch (error: any) {
return createToolResponse(`Failed to update theme in realm "${realm}": ${error.message}`);
}
}
};