-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Conversation
❌ Deploy Preview for dev-web-novu failed.
|
b50b9ae
to
f8d7c7f
Compare
@@ -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'; |
There was a problem hiding this comment.
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
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; | ||
} |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
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.', | ||
}) {} |
There was a problem hiding this comment.
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
$or: [ | ||
{ name: { $regex: regExpEscape(query), $options: 'i' } }, | ||
{ 'triggers.identifier': { $regex: regExpEscape(query), $options: 'i' } }, | ||
], |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this 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, |
There was a problem hiding this comment.
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.
$or: [ | ||
{ name: { $regex: regExpEscape(query), $options: 'i' } }, | ||
{ 'triggers.identifier': { $regex: regExpEscape(query), $options: 'i' } }, | ||
], |
There was a problem hiding this comment.
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.
d52e108
to
e0c7b5b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💪
What change does this PR introduce?
Allow searching workflows by name or trigger identifier.
Why was this change needed?
Other information (Screenshots)