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
fix: organization apis are accessible through api key #4619
Changes from all commits
a8b5f9a
9715dba
5a2de96
477500f
bfa60d2
d7518ee
1c25e05
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { MemberRoleEnum, MemberStatusEnum } from '@novu/shared'; | ||
import { IsArray, IsDate, IsObject, IsString, IsEnum } from 'class-validator'; | ||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; | ||
|
||
export class MemberUserDto { | ||
@ApiProperty() | ||
@IsString() | ||
_id: string; | ||
|
||
@ApiProperty() | ||
@IsString() | ||
firstName: string; | ||
|
||
@ApiProperty() | ||
@IsString() | ||
lastName: string; | ||
|
||
@ApiProperty() | ||
@IsString() | ||
email: string; | ||
} | ||
|
||
export class MemberInviteDTO { | ||
@ApiProperty() | ||
@IsString() | ||
email: string; | ||
|
||
@ApiProperty() | ||
@IsString() | ||
token: string; | ||
|
||
@ApiProperty() | ||
@IsDate() | ||
invitationDate: Date; | ||
|
||
@ApiPropertyOptional() | ||
@IsDate() | ||
answerDate?: Date; | ||
|
||
@ApiProperty() | ||
@IsString() | ||
_inviterId: string; | ||
} | ||
|
||
export class MemberResponseDto { | ||
@ApiProperty() | ||
@IsString() | ||
_id: string; | ||
|
||
@ApiProperty() | ||
@IsString() | ||
_userId: string; | ||
|
||
@ApiPropertyOptional() | ||
@IsObject() | ||
user?: MemberUserDto; | ||
|
||
@ApiPropertyOptional({ | ||
enum: MemberRoleEnum, | ||
isArray: true, | ||
}) | ||
@IsEnum(MemberRoleEnum) | ||
roles?: MemberRoleEnum; | ||
|
||
@ApiPropertyOptional() | ||
@IsObject() | ||
invite?: MemberInviteDTO; | ||
|
||
@ApiPropertyOptional({ | ||
enum: { ...MemberStatusEnum }, | ||
}) | ||
@IsEnum(MemberStatusEnum) | ||
memberStatus?: MemberStatusEnum; | ||
|
||
@ApiProperty() | ||
@IsString() | ||
_organizationId: string; | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,56 @@ | ||||||||||
import { PartnerTypeEnum, DirectionEnum } from '@novu/dal'; | ||||||||||
import { IsObject, IsArray, IsString, IsEnum } from 'class-validator'; | ||||||||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; | ||||||||||
import { UpdateBrandingDetailsDto } from './update-branding-details.dto'; | ||||||||||
|
||||||||||
export class IPartnerConfigurationResponseDto { | ||||||||||
@ApiPropertyOptional() | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
@IsArray() | ||||||||||
@IsString({ each: true }) | ||||||||||
projectIds?: string[]; | ||||||||||
|
||||||||||
@ApiProperty() | ||||||||||
@IsString() | ||||||||||
accessToken: string; | ||||||||||
|
||||||||||
@ApiProperty() | ||||||||||
@IsString() | ||||||||||
configurationId: string; | ||||||||||
|
||||||||||
@ApiPropertyOptional() | ||||||||||
@IsString() | ||||||||||
teamId: string; | ||||||||||
|
||||||||||
@ApiProperty({ | ||||||||||
enum: { ...PartnerTypeEnum }, | ||||||||||
description: 'Partner Type Enum', | ||||||||||
}) | ||||||||||
@IsEnum(PartnerTypeEnum) | ||||||||||
partnerType: PartnerTypeEnum; | ||||||||||
} | ||||||||||
|
||||||||||
export class OrganizationBrandingResponseDto extends UpdateBrandingDetailsDto { | ||||||||||
@ApiPropertyOptional({ | ||||||||||
enum: { ...DirectionEnum }, | ||||||||||
}) | ||||||||||
@IsString() | ||||||||||
direction?: DirectionEnum; | ||||||||||
} | ||||||||||
|
||||||||||
export class OrganizationResponseDto { | ||||||||||
@ApiProperty() | ||||||||||
@IsString() | ||||||||||
name: string; | ||||||||||
|
||||||||||
@ApiPropertyOptional() | ||||||||||
@IsString() | ||||||||||
logo?: string; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to add here @ApiPropertyOptional()? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added ✅ |
||||||||||
|
||||||||||
@ApiProperty() | ||||||||||
@IsObject() | ||||||||||
branding: OrganizationBrandingResponseDto; | ||||||||||
|
||||||||||
@ApiPropertyOptional() | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
@IsObject() | ||||||||||
partnerConfigurations: IPartnerConfigurationResponseDto[]; | ||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -13,7 +13,7 @@ import { | |||||
} from '@nestjs/common'; | ||||||
import { OrganizationEntity } from '@novu/dal'; | ||||||
import { IJwtPayload, MemberRoleEnum } from '@novu/shared'; | ||||||
import { ApiExcludeController, ApiTags } from '@nestjs/swagger'; | ||||||
import { ApiTags, ApiOperation, ApiParam } from '@nestjs/swagger'; | ||||||
import { Roles } from '../auth/framework/roles.decorator'; | ||||||
import { UserSession } from '../shared/framework/user.decorator'; | ||||||
import { CreateOrganizationDto } from './dtos/create-organization.dto'; | ||||||
|
@@ -39,12 +39,14 @@ import { RenameOrganization } from './usecases/rename-organization/rename-organi | |||||
import { RenameOrganizationDto } from './dtos/rename-organization.dto'; | ||||||
import { UpdateBrandingDetailsDto } from './dtos/update-branding-details.dto'; | ||||||
import { UpdateMemberRolesDto } from './dtos/update-member-roles.dto'; | ||||||
|
||||||
import { ExternalApiAccessible } from '../auth/framework/external-api.decorator'; | ||||||
import { ApiResponse } from '../shared/framework/response.decorator'; | ||||||
import { OrganizationBrandingResponseDto, OrganizationResponseDto } from './dtos/organization-response.dto'; | ||||||
import { MemberResponseDto } from './dtos/member-response.dto'; | ||||||
@Controller('/organizations') | ||||||
@UseInterceptors(ClassSerializerInterceptor) | ||||||
@UseGuards(JwtAuthGuard) | ||||||
@ApiTags('Organizations') | ||||||
@ApiExcludeController() | ||||||
export class OrganizationController { | ||||||
constructor( | ||||||
private createOrganizationUsecase: CreateOrganization, | ||||||
|
@@ -58,6 +60,11 @@ export class OrganizationController { | |||||
) {} | ||||||
|
||||||
@Post('/') | ||||||
@ExternalApiAccessible() | ||||||
@ApiResponse(OrganizationResponseDto, 201) | ||||||
@ApiOperation({ | ||||||
summary: 'Create an organization', | ||||||
}) | ||||||
async createOrganization( | ||||||
@UserSession() user: IJwtPayload, | ||||||
@Body() body: CreateOrganizationDto | ||||||
|
@@ -72,6 +79,11 @@ export class OrganizationController { | |||||
} | ||||||
|
||||||
@Get('/') | ||||||
@ExternalApiAccessible() | ||||||
@ApiResponse(OrganizationResponseDto, 200, true) | ||||||
@ApiOperation({ | ||||||
summary: 'Fetch all organizations', | ||||||
}) | ||||||
async getOrganizations(@UserSession() user: IJwtPayload): Promise<IGetOrganizationsDto> { | ||||||
const command = GetOrganizationsCommand.create({ | ||||||
userId: user._id, | ||||||
|
@@ -81,6 +93,11 @@ export class OrganizationController { | |||||
} | ||||||
|
||||||
@Get('/me') | ||||||
@ExternalApiAccessible() | ||||||
@ApiResponse(OrganizationResponseDto) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we add?
Suggested change
|
||||||
@ApiOperation({ | ||||||
summary: 'Fetch current organization details', | ||||||
}) | ||||||
async getMyOrganization(@UserSession() user: IJwtPayload): Promise<IGetMyOrganizationDto> { | ||||||
const command = GetMyOrganizationCommand.create({ | ||||||
userId: user._id, | ||||||
|
@@ -91,7 +108,13 @@ export class OrganizationController { | |||||
} | ||||||
|
||||||
@Delete('/members/:memberId') | ||||||
@ExternalApiAccessible() | ||||||
@Roles(MemberRoleEnum.ADMIN) | ||||||
@ApiResponse(MemberResponseDto) | ||||||
@ApiOperation({ | ||||||
summary: 'Remove a member from organization using memberId', | ||||||
}) | ||||||
@ApiParam({ name: 'memberId', type: String, required: true }) | ||||||
async removeMember(@UserSession() user: IJwtPayload, @Param('memberId') memberId: string) { | ||||||
return await this.removeMemberUsecase.execute( | ||||||
RemoveMemberCommand.create({ | ||||||
|
@@ -103,7 +126,13 @@ export class OrganizationController { | |||||
} | ||||||
|
||||||
@Put('/members/:memberId/roles') | ||||||
@ExternalApiAccessible() | ||||||
@Roles(MemberRoleEnum.ADMIN) | ||||||
@ApiResponse(MemberResponseDto) | ||||||
@ApiOperation({ | ||||||
summary: 'Update a member role to admin', | ||||||
}) | ||||||
@ApiParam({ name: 'memberId', type: String, required: true }) | ||||||
async updateMemberRoles( | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to add here? |
||||||
@UserSession() user: IJwtPayload, | ||||||
@Param('memberId') memberId: string, | ||||||
|
@@ -120,6 +149,11 @@ export class OrganizationController { | |||||
} | ||||||
|
||||||
@Get('/members') | ||||||
@ExternalApiAccessible() | ||||||
@ApiResponse(MemberResponseDto, 200, true) | ||||||
@ApiOperation({ | ||||||
summary: 'Fetch all members of current organizations', | ||||||
}) | ||||||
async getMember(@UserSession() user: IJwtPayload) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to add here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added MemberDto ✅ |
||||||
return await this.getMembers.execute( | ||||||
GetMembersCommand.create({ | ||||||
|
@@ -130,19 +164,12 @@ export class OrganizationController { | |||||
); | ||||||
} | ||||||
|
||||||
@Post('/members/invite') | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure what this endpoint was responsible of but i guess we don't need @post('/members/invite') that returns members. 👏 |
||||||
@Roles(MemberRoleEnum.ADMIN) | ||||||
async inviteMember(@UserSession() user: IJwtPayload) { | ||||||
return await this.getMembers.execute( | ||||||
GetMembersCommand.create({ | ||||||
user, | ||||||
userId: user._id, | ||||||
organizationId: user.organizationId, | ||||||
}) | ||||||
); | ||||||
} | ||||||
|
||||||
@Put('/branding') | ||||||
@ExternalApiAccessible() | ||||||
@ApiResponse(OrganizationBrandingResponseDto) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be with 200?
Suggested change
|
||||||
@ApiOperation({ | ||||||
summary: 'Update organization branding details', | ||||||
}) | ||||||
async updateBrandingDetails(@UserSession() user: IJwtPayload, @Body() body: UpdateBrandingDetailsDto) { | ||||||
return await this.updateBrandingDetailsUsecase.execute( | ||||||
UpdateBrandingDetailsCommand.create({ | ||||||
|
@@ -158,7 +185,12 @@ export class OrganizationController { | |||||
} | ||||||
|
||||||
@Patch('/') | ||||||
@ExternalApiAccessible() | ||||||
@Roles(MemberRoleEnum.ADMIN) | ||||||
@ApiResponse(RenameOrganizationDto) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be with 200?
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ApiResponse() has 200 as default status code There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, good to know :) |
||||||
@ApiOperation({ | ||||||
summary: 'Rename organization name', | ||||||
}) | ||||||
async renameOrganization(@UserSession() user: IJwtPayload, @Body() body: RenameOrganizationDto) { | ||||||
return await this.renameOrganizationUsecase.execute( | ||||||
RenameOrganizationCommand.create({ | ||||||
|
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.