Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): search workflows by name or trigger identifier #5268

Merged
merged 2 commits into from
Mar 11, 2024

Conversation

LetItRock
Copy link
Contributor

What change does this PR introduce?

Allow searching workflows by name or trigger identifier.

Why was this change needed?

Other information (Screenshots)

Screenshot 2024-03-07 at 00 52 29
Screenshot 2024-03-07 at 00 52 59

@LetItRock LetItRock requested a review from djabarovgeorge March 6, 2024 23:54
@LetItRock LetItRock self-assigned this Mar 6, 2024
Copy link

linear bot commented Mar 6, 2024

Copy link

netlify bot commented Mar 6, 2024

Deploy Preview for dev-web-novu failed.

Name Link
🔨 Latest commit e0c7b5b
🔍 Latest deploy log https://app.netlify.com/sites/dev-web-novu/deploys/65eb15dc4ee3fa0008b7bca0

@LetItRock LetItRock force-pushed the nv-3229-api-search-workflows branch from b50b9ae to f8d7c7f Compare March 7, 2024 00:05
@@ -2,10 +2,15 @@ import { ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { IsInt, Max, Min } from 'class-validator';

export type Constructor<I> = new (...args: any[]) => I;
import { Constructor } from '../types';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

moved to a shared type

Comment on lines +12 to +33
export function PaginationWithFiltersRequestDto({
defaultLimit = 10,
maxLimit = 100,
queryDescription,
}: {
defaultLimit: number;
maxLimit: number;
queryDescription: string;
}): Constructor<IPaginationWithFilters> {
class PaginationWithFiltersRequest extends PaginationRequestDto(defaultLimit, maxLimit) {
@ApiPropertyOptional({
type: String,
required: false,
description: `A query string to filter the results. ${queryDescription}`,
})
@IsOptional()
@IsString()
query?: string;
}

return PaginationWithFiltersRequest;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

extended the PaginationRequestDto class with a query field, allowing also to pass a different description

@@ -485,10 +485,10 @@ export class SubscribersController {
organizationId: user.organizationId,
environmentId: user.environmentId,
subscriberId: subscriberId,
page: query.page != null ? parseInt(query.page) : 0,
page: query.page != null ? parseInt(query.page as any) : 0,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

When I typed the pagination interface to the correct field type, these and a few more places started to raise the TS error that the number is not assignable to a string. I didn't want to change it, like by removing parseInt because of the risk of breaking something.

Copy link
Contributor

Choose a reason for hiding this comment

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

are we certain that PaginationRequestDto paras are validated in the transport dto phase, if we do then I think it should be saved to remove the parsing because if it is not number @ISINT() will throw an exception.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, just checked it's covered with query params to dto transformation :) so the fields are integers

Comment on lines +3 to +7
export class WorkflowsRequestDto extends PaginationWithFiltersRequestDto({
defaultLimit: 10,
maxLimit: 100,
queryDescription: 'It allows filtering based on either the name or trigger identifier of the workflow items.',
}) {}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is how that new interface is used

Comment on lines +172 to +175
$or: [
{ name: { $regex: regExpEscape(query), $options: 'i' } },
{ 'triggers.identifier': { $regex: regExpEscape(query), $options: 'i' } },
],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

allow searching by the name or trigger identifier case-insensitive

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, i missed this part of the search implementation.
if we want to search by both name and identifier in the same query (by OR operator), as far as I know, we need to make sure we have an index for both (all cases) meaning:

  • user query by name, index _environmentId.name
  • user query by name and identifier, index _environmentId.identifier.name (covers _environmentId.identifier and _environmentId.identifier.name )

This is because if we have only _environmentId.identifier.name we could not use it for queries by name because we would be missing the identifier in the index.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I do understand what you mean. I will add it, thanks! 🙌

I see that we also have _organizationId.triggers.identifier, but I couldn't find any references in the code where we use _organizationId and identifier. Do we need it?

Also, in Atlas _environmentId_1_triggers.identifier_1 means that we just have to extend it right?

Copy link
Contributor

Choose a reason for hiding this comment

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

The only places i found that depend on _organizationId in their query are blueprint-related which are located in:

  • NotificationTemplateRepository.findBlueprintByTriggerIdentifier
  • NotificationTemplateRepository.getBlueprintList
    we could easily refactor the code to use environmentId instead, however for now I would keep the index so we won't scan the collections until we refactor the code.

Thats right we could extend _environmentId_1_triggers.identifier_1 to _environmentId_1_triggers.identifier_1_name

@LetItRock LetItRock changed the title feat(api): search workflows by name or trigger identifier [DRAFT] feat(api): search workflows by name or trigger identifier Mar 7, 2024
Copy link
Contributor

@djabarovgeorge djabarovgeorge left a comment

Choose a reason for hiding this comment

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

Amazing work, left couple of comment for your review :)

@@ -485,10 +485,10 @@ export class SubscribersController {
organizationId: user.organizationId,
environmentId: user.environmentId,
subscriberId: subscriberId,
page: query.page != null ? parseInt(query.page) : 0,
page: query.page != null ? parseInt(query.page as any) : 0,
Copy link
Contributor

Choose a reason for hiding this comment

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

are we certain that PaginationRequestDto paras are validated in the transport dto phase, if we do then I think it should be saved to remove the parsing because if it is not number @ISINT() will throw an exception.

Comment on lines +172 to +175
$or: [
{ name: { $regex: regExpEscape(query), $options: 'i' } },
{ 'triggers.identifier': { $regex: regExpEscape(query), $options: 'i' } },
],
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, i missed this part of the search implementation.
if we want to search by both name and identifier in the same query (by OR operator), as far as I know, we need to make sure we have an index for both (all cases) meaning:

  • user query by name, index _environmentId.name
  • user query by name and identifier, index _environmentId.identifier.name (covers _environmentId.identifier and _environmentId.identifier.name )

This is because if we have only _environmentId.identifier.name we could not use it for queries by name because we would be missing the identifier in the index.

@LetItRock LetItRock force-pushed the nv-3229-api-search-workflows branch from d52e108 to e0c7b5b Compare March 8, 2024 13:42
Copy link
Contributor

@djabarovgeorge djabarovgeorge left a comment

Choose a reason for hiding this comment

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

💪

@LetItRock LetItRock changed the title [DRAFT] feat(api): search workflows by name or trigger identifier feat(api): search workflows by name or trigger identifier Mar 11, 2024
@LetItRock LetItRock merged commit 8cc0f69 into next Mar 11, 2024
25 of 31 checks passed
@LetItRock LetItRock deleted the nv-3229-api-search-workflows branch March 11, 2024 08:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants