Skip to content
Draft
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
33 changes: 33 additions & 0 deletions src/controllers/llmo/llmo.js
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,38 @@ function LlmoController(ctx) {
}
};

// Handles requests to update per-site country code ignore list for CDN logs reports
const patchLlmoCountryCodeIgnoreList = async (context) => {
const { log } = context;
const { data } = context;
const { siteId } = context.params;

try {
if (!accessControlUtil.isLLMOAdministrator()) {
return forbidden('Only LLMO administrators can update the country code ignore list');
}

const siteValidation = await getSiteAndValidateLlmo(context);
if (siteValidation.status) return siteValidation;
const { site, config } = siteValidation;

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

const { countryCodeIgnoreList } = data;

config.updateLlmoCountryCodeIgnoreList(countryCodeIgnoreList);

await saveSiteConfig(site, config, log, 'updating country code ignore list');

return ok(config.getLlmoConfig().countryCodeIgnoreList || []);
} catch (error) {
log.error(`Error updating country code ignore list for siteId: ${siteId}, error: ${error.message}`);
return badRequest(error.message);
}
};

/**
* Onboards a new customer to LLMO.
* This endpoint handles the complete onboarding process for net new customers
Expand Down Expand Up @@ -1702,6 +1734,7 @@ function LlmoController(ctx) {
patchLlmoCustomerIntent,
patchLlmoCdnLogsFilter,
patchLlmoCdnBucketConfig,
patchLlmoCountryCodeIgnoreList,
updateLlmoConfig,
onboardCustomer,
offboardCustomer,
Expand Down
1 change: 1 addition & 0 deletions src/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ export default function getRouteHandlers(
'PATCH /sites/:siteId/llmo/customer-intent/:intentKey': llmoController.patchLlmoCustomerIntent,
'PATCH /sites/:siteId/llmo/cdn-logs-filter': llmoController.patchLlmoCdnLogsFilter,
'PATCH /sites/:siteId/llmo/cdn-logs-bucket-config': llmoController.patchLlmoCdnBucketConfig,
'PATCH /sites/:siteId/llmo/country-code-ignore-list': llmoController.patchLlmoCountryCodeIgnoreList,
'GET /sites/:siteId/llmo/global-sheet-data/:configName': llmoController.getLlmoGlobalSheetData,
'GET /sites/:siteId/llmo/rationale': llmoController.getLlmoRationale,
'GET /sites/:siteId/llmo/brand-claims': llmoController.getBrandClaims,
Expand Down
72 changes: 72 additions & 0 deletions test/controllers/llmo/llmo.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ describe('LlmoController', () => {
updateLlmoCustomerIntent: sinon.stub(),
updateLlmoCdnlogsFilter: sinon.stub(),
updateLlmoCdnBucketConfig: sinon.stub(),
updateLlmoCountryCodeIgnoreList: sinon.stub(),
addLlmoTag: sinon.stub(),
state: { llmo: mockLlmoConfig },
getSlackConfig: sinon.stub().returns(null),
Expand Down Expand Up @@ -2459,6 +2460,77 @@ describe('LlmoController', () => {
});
});

describe('patchLlmoCountryCodeIgnoreList', () => {
beforeEach(() => {
mockContext.data = {
countryCodeIgnoreList: ['PS', 'AD'],
};
});

it('should update country code ignore list successfully', async () => {
const result = await controller.patchLlmoCountryCodeIgnoreList(mockContext);

expect(result.status).to.equal(200);
expect(mockConfig.updateLlmoCountryCodeIgnoreList).to.have.been.calledWith(
mockContext.data.countryCodeIgnoreList,
);
});

it('should return bad request when no data provided', async () => {
mockContext.data = null;

const result = await controller.patchLlmoCountryCodeIgnoreList(mockContext);

expect(result.status).to.equal(400);
});

it('should handle errors and log them', async () => {
mockDataAccess.Site.findById.rejects(new Error('Database error'));

const result = await controller.patchLlmoCountryCodeIgnoreList(mockContext);

expect(result.status).to.equal(400);
expect(mockLog.error).to.have.been.calledWith(
`Error updating country code ignore list for siteId: ${TEST_SITE_ID}, error: Database error`,
);
});

it('should return empty array when getLlmoConfig().countryCodeIgnoreList is null', async () => {
mockConfig.getLlmoConfig.returns({
dataFolder: TEST_FOLDER,
brand: TEST_BRAND,
countryCodeIgnoreList: null,
});

const result = await controller.patchLlmoCountryCodeIgnoreList(mockContext);

expect(result.status).to.equal(200);
const body = await result.json();
expect(body).to.be.an('array');
});

it('should return 403 when user is not LLMO administrator', async () => {
const LlmoControllerNoAdmin = await esmock('../../../src/controllers/llmo/llmo.js', {
'../../../src/support/access-control-util.js': createMockAccessControlUtil(true, true, false),
'@adobe/spacecat-shared-http-utils': mockHttpUtils,
...getCommonMocks(),
});

const controllerNoAdmin = LlmoControllerNoAdmin(mockContext);
const result = await controllerNoAdmin.patchLlmoCountryCodeIgnoreList(mockContext);

expect(result.status).to.equal(403);
const responseBody = await result.json();
expect(responseBody.message).to.equal('Only LLMO administrators can update the country code ignore list');
});

it('should return 404 when site is not found', async () => {
mockDataAccess.Site.findById.resolves(null);
const result = await controller.patchLlmoCountryCodeIgnoreList(mockContext);
expect(result.status).to.equal(404);
});
});

describe('onboardCustomer', () => {
let mockOrganization;
let mockNewSite;
Expand Down
4 changes: 4 additions & 0 deletions test/routes/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ describe('getRouteHandlers', () => {
patchLlmoCustomerIntent: () => null,
patchLlmoCdnLogsFilter: () => null,
patchLlmoCdnBucketConfig: () => null,
patchLlmoCountryCodeIgnoreList: () => null,
onboardCustomer: () => null,
offboardCustomer: () => null,
queryFiles: () => null,
Expand Down Expand Up @@ -683,6 +684,7 @@ describe('getRouteHandlers', () => {
'PATCH /sites/:siteId/llmo/cdn-logs-filter',
'POST /sites/:siteId/sandbox/audit',
'PATCH /sites/:siteId/llmo/cdn-logs-bucket-config',
'PATCH /sites/:siteId/llmo/country-code-ignore-list',
'GET /sites/:siteId/llmo/global-sheet-data/:configName',
'GET /sites/:siteId/llmo/rationale',
'GET /sites/:siteId/llmo/brand-claims',
Expand Down Expand Up @@ -930,6 +932,8 @@ describe('getRouteHandlers', () => {
expect(dynamicRoutes['GET /sites/:siteId/traffic/paid/url-page-type'].paramNames).to.deep.equal(['siteId']);
expect(dynamicRoutes['PATCH /sites/:siteId/llmo/cdn-logs-bucket-config'].handler).to.equal(mockLlmoController.patchLlmoCdnBucketConfig);
expect(dynamicRoutes['PATCH /sites/:siteId/llmo/cdn-logs-bucket-config'].paramNames).to.deep.equal(['siteId']);
expect(dynamicRoutes['PATCH /sites/:siteId/llmo/country-code-ignore-list'].handler).to.equal(mockLlmoController.patchLlmoCountryCodeIgnoreList);
expect(dynamicRoutes['PATCH /sites/:siteId/llmo/country-code-ignore-list'].paramNames).to.deep.equal(['siteId']);
expect(dynamicRoutes['GET /sites/:siteId/llmo/global-sheet-data/:configName'].handler).to.equal(mockLlmoController.getLlmoGlobalSheetData);
expect(dynamicRoutes['GET /sites/:siteId/llmo/global-sheet-data/:configName'].paramNames).to.deep.equal(['siteId', 'configName']);
expect(dynamicRoutes['GET /sites/:siteId/llmo/rationale'].handler).to.equal(mockLlmoController.getLlmoRationale);
Expand Down
Loading