Skip to content

CCM-15562: Add campaignId check to update routing config#899

Draft
m-salaudeen wants to merge 2 commits intomainfrom
feature/CCM-15562_matching_campaign_id_api
Draft

CCM-15562: Add campaignId check to update routing config#899
m-salaudeen wants to merge 2 commits intomainfrom
feature/CCM-15562_matching_campaign_id_api

Conversation

@m-salaudeen
Copy link
Copy Markdown
Contributor

Description

Context

Type of changes

  • Refactoring (non-breaking change)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would change existing functionality)
  • Bug fix (non-breaking change which fixes an issue)

Checklist

  • I am familiar with the contributing guidelines
  • I have followed the code style of the project
  • I have added tests to cover my changes
  • I have updated the documentation accordingly
  • This PR is a result of pair or mob programming

Sensitive Information Declaration

To ensure the utmost confidentiality and protect your and others privacy, we kindly ask you to NOT including PII (Personal Identifiable Information) / PID (Personal Identifiable Data) or any other sensitive data in this PR (Pull Request) and the codebase changes. We will remove any PR that do contain any sensitive information. We really appreciate your cooperation in this matter.

  • I confirm that neither PII/PID nor sensitive data are included in this PR and the codebase changes.

@m-salaudeen m-salaudeen self-assigned this Apr 8, 2026
@m-salaudeen m-salaudeen added the skip-trivy-package Skip the Trivy Package Scan label Apr 8, 2026
// get the letter templateIds from the validated and make sure they are the same type
const templateIds =
validated.cascade
?.map((c) => c.conditionalTemplates?.map((t) => t.templateId))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

you're missing the non-conditional templates, those would come from the 'defaultTemplateId' field

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

agree with @alexnuttall
only conditionalTemplates IDs are extracted here, we also need the defaultTemplateId on each cascade item
the repository layer already has an extractTemplateIds helper that handles both here

// fetch all the templates of these templateIds
const templates = await Promise.all(
templateIds.map((templateId) =>
this.templateClient.getTemplate(templateId as string, user)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think using BatchGetItem would be better than many concurrent individual get requests

private readonly routingConfigRepository: RoutingConfigRepository,
private readonly clientConfigRepository: ClientConfigRepository
private readonly clientConfigRepository: ClientConfigRepository,
private readonly templateClient: TemplateClient
Copy link
Copy Markdown
Contributor

@alexnuttall alexnuttall Apr 8, 2026

Choose a reason for hiding this comment

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

you haven't updated the 'container' file which constructs this class, so this can't work currently.

I also think it might not be possible for the two repositories to mutually depend on each other?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

agree, need to update here

);
}

// if the payload (validateResult) has a campaignId, check it matches with the template campaignId
Copy link
Copy Markdown
Contributor

@alexnuttall alexnuttall Apr 8, 2026

Choose a reason for hiding this comment

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

Since we're blocking all updates to a routing config's campaign ID soon, it might not be worth us implementing stuff specific to handling updates which combine campaignId and templates in the payload. What do you think @nicki-nhs ?

If we are handling those cases, this doesn't seem correct. If there is a new campaignId in the payload, we can't expect the existing database entry to have a matching ID already. The expect condition should only be applied if the payload is not updating the campaignId

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I agree
we should just be fetching the routing config from the ID and using the campaign ID on that to validate against

Copy link
Copy Markdown
Contributor

@alexnuttall alexnuttall Apr 10, 2026

Choose a reason for hiding this comment

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

I don't think there's necesarily any need to fetch the routing config. A conditional update is sufficient - it's just the (current) support for patching campaign which makes things more complicated

Then again the code might be a little simpler with pre-fetching, at a perfomance cost

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@nicki-nhs since we're already coverting conditional update errors into failure codes I don't see the problem with adding 'campaign mismatch' there?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

yeah could do, what are you thinking the condition would be that we'd be able to use to differentiate it?
I think its equally valid in this file too, with the other campaign ID validation on L137+
but dont feel strongly either way

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You would need to pass through expectedCampaign into handleError, similarly to expectedLockNumber.

There would still be a need to return a similar error from the client layer, because you need to handle a patch payload where some template campaigns don't match others.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

poss a monday discussion but I think I dont understand the need to catch that specifically tho, if they dont match the routing config, its irrelevant if they match each other isnt it?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You need select one to use as the condition, if there are multiple, you already know the update is invalid

private readonly routingConfigRepository: RoutingConfigRepository,
private readonly clientConfigRepository: ClientConfigRepository
private readonly clientConfigRepository: ClientConfigRepository,
private readonly templateClient: TemplateClient
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should be using TemplateRepository rather than the client

Comment on lines +111 to +128
// check all the templates have campaignIds if they don't have campaignIds, reject the request
if (templates.some((template) => !template.data?.campaignId)) {
return failure(
ErrorCase.VALIDATION_FAILED,
'One or more templates do not have a campaign ID'
);
}

// if they do check that those campaignIds are the same if not reject the request
const uniqueCampaignIds = [
...new Set(templates.map((t) => t.data?.campaignId)),
];
if (uniqueCampaignIds.length > 1) {
return failure(
ErrorCase.VALIDATION_FAILED,
'All templates must have the same campaign ID'
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm happy to discuss this but I'm not sure we need these additional checks, given if we check if each one matches the campaign ID on the routing config, the case where its missing or they dont all match each other would be caught anyway. feels like a lot of code to add for not much benefit imo

const expectedCampaignId =
validated.campaignId === templates[0]?.data?.campaignId
? validated.campaignId
: templates[0]?.data?.campaignId;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the source of truth for the expected campaignId should be the routing config already in the database, not the templates being added. the approach should be: fetch the existing routing config first, get its campaignId then validate that the templates match it

user,
lockNumberValidation.data
lockNumberValidation.data,
expectedCampaignId as string
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I dont think we want this

UpdateRoutingConfig,
} from 'nhs-notify-web-template-management-types';
import { ClientConfigRepository } from '../../infra/client-config-repository';
import { TemplateClient } from '@backend-api/app/template-client';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

needs updating to be the repository
and tests missing in here

Comment on lines +118 to +119
} else {
update.expectCampaignId(expectedCampaignId);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

shouldnt need this, can revert this file

Comment on lines +45 to +49
expectCampaignId(expectedCampaignId: string) {
this.updateBuilder.conditions.and('campaignId', '=', expectedCampaignId);
return this;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can revert this, shouldnt be needed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-trivy-package Skip the Trivy Package Scan

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants