Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 81 additions & 50 deletions src/controllers/llmo/llmo.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* governing permissions and limitations under the License.
*/

import { ok, badRequest } from '@adobe/spacecat-shared-http-utils';
import { ok, badRequest, forbidden } from '@adobe/spacecat-shared-http-utils';
import {
SPACECAT_USER_AGENT,
tracingFetch as fetch,
Expand Down Expand Up @@ -437,91 +437,122 @@ function LlmoController(ctx) {

// Handles requests to the LLMO customer intent endpoint, returns customer intent array
const getLlmoCustomerIntent = async (context) => {
const { llmoConfig } = await getSiteAndValidateLlmo(context);
return ok(llmoConfig.customerIntent || []);
try {
const { llmoConfig } = await getSiteAndValidateLlmo(context);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handle it in getSiteAndValidateLlmo method itself rather everyplace

return ok(llmoConfig.customerIntent || []);
} catch (error) {
if (error.message === 'Only users belonging to the organization can view its sites') {
return forbidden(error.message);
}
return badRequest(error.message);
}
};

// Handles requests to the LLMO customer intent endpoint, adds new customer intent items
const addLlmoCustomerIntent = async (context) => {
const { log } = context;
const { site, config } = await getSiteAndValidateLlmo(context);

const newCustomerIntent = context.data;
if (!Array.isArray(newCustomerIntent)) {
return badRequest('Customer intent must be provided as an array');
}

// Get existing customer intent keys to check for duplicates
const existingCustomerIntent = config.getLlmoCustomerIntent() || [];
const existingKeys = new Set(existingCustomerIntent.map((item) => item.key));
const newKeys = new Set();
try {
const { site, config } = await getSiteAndValidateLlmo(context);

// Validate structure of each customer intent item and check for duplicates
for (const intent of newCustomerIntent) {
if (!hasText(intent.key) || !hasText(intent.value)) {
return badRequest('Each customer intent item must have both key and value properties');
const newCustomerIntent = context.data;
if (!Array.isArray(newCustomerIntent)) {
return badRequest('Customer intent must be provided as an array');
}

if (existingKeys.has(intent.key)) {
return badRequest(`Customer intent key '${intent.key}' already exists`);
}
// Get existing customer intent keys to check for duplicates
const existingCustomerIntent = config.getLlmoCustomerIntent() || [];
const existingKeys = new Set(existingCustomerIntent.map((item) => item.key));
const newKeys = new Set();

if (newKeys.has(intent.key)) {
return badRequest(`Duplicate customer intent key '${intent.key}' in request`);
}
// Validate structure of each customer intent item and check for duplicates
for (const intent of newCustomerIntent) {
if (!hasText(intent.key) || !hasText(intent.value)) {
return badRequest('Each customer intent item must have both key and value properties');
}

newKeys.add(intent.key);
}
if (existingKeys.has(intent.key)) {
return badRequest(`Customer intent key '${intent.key}' already exists`);
}

if (newKeys.has(intent.key)) {
return badRequest(`Duplicate customer intent key '${intent.key}' in request`);
}

config.addLlmoCustomerIntent(newCustomerIntent);
await saveSiteConfig(site, config, log, 'adding customer intent');
newKeys.add(intent.key);
}

// return the updated llmoConfig customer intent
return ok(config.getLlmoConfig().customerIntent || []);
config.addLlmoCustomerIntent(newCustomerIntent);
await saveSiteConfig(site, config, log, 'adding customer intent');

// return the updated llmoConfig customer intent
return ok(config.getLlmoConfig().customerIntent || []);
} catch (error) {
if (error.message === 'Only users belonging to the organization can view its sites') {
return forbidden(error.message);
}
return badRequest(error.message);
}
};

// Handles requests to the LLMO customer intent endpoint, removes a customer intent item
const removeLlmoCustomerIntent = async (context) => {
const { log } = context;
const { intentKey } = context.params;
const { site, config } = await getSiteAndValidateLlmo(context);

validateCustomerIntentKey(config, intentKey);
try {
const { site, config } = await getSiteAndValidateLlmo(context);

validateCustomerIntentKey(config, intentKey);

// remove the customer intent using the config method
config.removeLlmoCustomerIntent(intentKey);
// remove the customer intent using the config method
config.removeLlmoCustomerIntent(intentKey);

await saveSiteConfig(site, config, log, 'removing customer intent');
await saveSiteConfig(site, config, log, 'removing customer intent');

// return the updated llmoConfig customer intent
return ok(config.getLlmoConfig().customerIntent || []);
// return the updated llmoConfig customer intent
return ok(config.getLlmoConfig().customerIntent || []);
} catch (error) {
if (error.message === 'Only users belonging to the organization can view its sites') {
return forbidden(error.message);
}
return badRequest(error.message);
}
};

// Handles requests to the LLMO customer intent endpoint, updates a customer intent item
const patchLlmoCustomerIntent = async (context) => {
const { log } = context;
const { intentKey } = context.params;
const { data } = context;
const { site, config } = await getSiteAndValidateLlmo(context);

validateCustomerIntentKey(config, intentKey);
try {
const { site, config } = await getSiteAndValidateLlmo(context);

validateCustomerIntentKey(config, intentKey);

// Validate the update data
if (!isObject(data)) {
return badRequest('Update data must be provided as an object');
}
// Validate the update data
if (!isObject(data)) {
return badRequest('Update data must be provided as an object');
}

if (!hasText(data.value)) {
return badRequest('Customer intent value must be a non-empty string');
}
if (!hasText(data.value)) {
return badRequest('Customer intent value must be a non-empty string');
}

// update the customer intent using the config method
config.updateLlmoCustomerIntent(intentKey, data);
// update the customer intent using the config method
config.updateLlmoCustomerIntent(intentKey, data);

await saveSiteConfig(site, config, log, 'updating customer intent');
await saveSiteConfig(site, config, log, 'updating customer intent');

// return the updated llmoConfig customer intent
return ok(config.getLlmoConfig().customerIntent || []);
// return the updated llmoConfig customer intent
return ok(config.getLlmoConfig().customerIntent || []);
} catch (error) {
if (error.message === 'Only users belonging to the organization can view its sites') {
return forbidden(error.message);
}
return badRequest(error.message);
}
};

// Handles requests to the LLMO CDN logs filter endpoint, updates CDN logs filter configuration
Expand Down
7 changes: 6 additions & 1 deletion src/support/access-control-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,12 @@ export default class AccessControlUtil {
} else if (entity instanceof Organization) {
org = entity;
}
await this.validateEntitlement(org, site, productCode);
try {
await this.validateEntitlement(org, site, productCode);
} catch (e) {
this.log.error(`Error validating entitlement for ${entity.getId()}: ${e.message}`);
return false;
}
}
if (subService.length > 0) {
return hasOrgAccess && authInfo.hasScope('user', `${SERVICE_CODE}_${subService}`);
Expand Down
Loading