import { z } from 'zod';
import { makeAuthenticatedRequest, createToolResponse } from '../../utils/apiHelpers.js';
import { formatSuccess } from '../../utils/responseHelpers.js';
import { REALMS, safePathSegmentSchema } from '../../utils/validationHelpers.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: safePathSegmentSchema.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}`);
}
}
};