Skip to content
This repository has been archived by the owner on Apr 19, 2023. It is now read-only.

Commit

Permalink
✨ Add webhooks module
Browse files Browse the repository at this point in the history
  • Loading branch information
AnandChowdhary committed Nov 7, 2020
1 parent 5a1cb70 commit 47869cd
Show file tree
Hide file tree
Showing 5 changed files with 316 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { SessionsModule } from './modules/sessions/sessions.module';
import { StripeModule } from './modules/stripe/stripe.module';
import { TasksModule } from './modules/tasks/tasks.module';
import { UsersModule } from './modules/users/users.module';
import { WebhooksModule } from './modules/webhooks/webhooks.module';

@Module({
imports: [
Expand Down Expand Up @@ -61,6 +62,7 @@ import { UsersModule } from './modules/users/users.module';
MembershipsModule,
StripeModule,
AuditLogsModule,
WebhooksModule,
],
providers: [
{
Expand Down
109 changes: 109 additions & 0 deletions src/modules/webhooks/webhooks.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import {
Body,
Controller,
Delete,
Get,
Param,
ParseIntPipe,
Patch,
Post,
Put,
Query,
} from '@nestjs/common';
import { webhooks } from '@prisma/client';
import { Expose } from '../prisma/prisma.interface';
import { CursorPipe } from '../../pipes/cursor.pipe';
import { OptionalIntPipe } from '../../pipes/optional-int.pipe';
import { OrderByPipe } from '../../pipes/order-by.pipe';
import { WherePipe } from '../../pipes/where.pipe';
import { AuditLog } from '../audit-logs/audit-log.decorator';
import { Scopes } from '../auth/scope.decorator';
import {
CreateWebhookDto,
ReplaceWebhookDto,
UpdateWebhookDto,
} from './webhooks.dto';
import { WebhooksService } from './webhooks.service';

@Controller('groups/:groupId/webhooks')
export class WebhookController {
constructor(private webhooksService: WebhooksService) {}

@Post()
@AuditLog('create-webhook')
@Scopes('group-{groupId}:write-webhook-*')
async create(
@Param('groupId', ParseIntPipe) groupId: number,
@Body() data: CreateWebhookDto,
): Promise<Expose<webhooks>> {
return this.webhooksService.createWebhook(groupId, data);
}

@Get()
@Scopes('group-{groupId}:read-webhook-*')
async getAll(
@Param('groupId', ParseIntPipe) groupId: number,
@Query('skip', OptionalIntPipe) skip?: number,
@Query('take', OptionalIntPipe) take?: number,
@Query('cursor', CursorPipe) cursor?: Record<string, number | string>,
@Query('where', WherePipe) where?: Record<string, number | string>,
@Query('orderBy', OrderByPipe) orderBy?: Record<string, 'asc' | 'desc'>,
): Promise<Expose<webhooks>[]> {
return this.webhooksService.getWebhooks(groupId, {
skip,
take,
orderBy,
cursor,
where,
});
}

@Get('scopes')
@Scopes('group-{groupId}:write-webhook-*')
async scopes(
@Param('groupId', ParseIntPipe) groupId: number,
): Promise<Record<string, string>> {
return this.webhooksService.getWebhookScopes(groupId);
}

@Get(':id')
@Scopes('group-{groupId}:read-webhook-{id}')
async get(
@Param('groupId', ParseIntPipe) groupId: number,
@Param('id', ParseIntPipe) id: number,
): Promise<Expose<webhooks>> {
return this.webhooksService.getWebhook(groupId, Number(id));
}

@Patch(':id')
@AuditLog('update-webhook')
@Scopes('group-{groupId}:write-webhook-{id}')
async update(
@Body() data: UpdateWebhookDto,
@Param('groupId', ParseIntPipe) groupId: number,
@Param('id', ParseIntPipe) id: number,
): Promise<Expose<webhooks>> {
return this.webhooksService.updateWebhook(groupId, Number(id), data);
}

@Put(':id')
@AuditLog('update-webhook')
@Scopes('group-{groupId}:write-webhook-{id}')
async replace(
@Body() data: ReplaceWebhookDto,
@Param('groupId', ParseIntPipe) groupId: number,
@Param('id', ParseIntPipe) id: number,
): Promise<Expose<webhooks>> {
return this.webhooksService.updateWebhook(groupId, Number(id), data);
}

@Delete(':id')
@AuditLog('delete-webhook')
@Scopes('group-{groupId}:delete-webhook-{id}')
async remove(
@Param('groupId', ParseIntPipe) groupId: number,
@Param('id', ParseIntPipe) id: number,
): Promise<Expose<webhooks>> {
return this.webhooksService.deleteWebhook(groupId, Number(id));
}
}
73 changes: 73 additions & 0 deletions src/modules/webhooks/webhooks.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {
IsArray,
IsBoolean,
IsNotEmpty,
IsOptional,
IsString,
} from 'class-validator';

export class CreateWebhookDto {
@IsString()
@IsNotEmpty()
url: string;

@IsString()
@IsNotEmpty()
event: string;

@IsString()
@IsOptional()
contentType?: string;

@IsBoolean()
@IsOptional()
isActive?: boolean;

@IsString()
@IsOptional()
secret?: string;
}

export class UpdateWebhookDto {
@IsString()
@IsOptional()
url?: string;

@IsString()
@IsOptional()
event?: string;

@IsString()
@IsOptional()
contentType?: string;

@IsBoolean()
@IsOptional()
isActive?: boolean;

@IsString()
@IsOptional()
secret?: string;
}

export class ReplaceWebhookDto {
@IsString()
@IsNotEmpty()
url!: string;

@IsString()
@IsNotEmpty()
event!: string;

@IsString()
@IsOptional()
contentType!: string;

@IsBoolean()
@IsOptional()
isActive!: boolean;

@IsString()
@IsOptional()
secret!: string;
}
13 changes: 13 additions & 0 deletions src/modules/webhooks/webhooks.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { PrismaModule } from '../prisma/prisma.module';
import { StripeModule } from '../stripe/stripe.module';
import { TokensModule } from '../tokens/tokens.module';
import { WebhookController } from './webhooks.controller';
import { WebhooksService } from './webhooks.service';

@Module({
imports: [PrismaModule, TokensModule, StripeModule],
controllers: [WebhookController],
providers: [WebhooksService],
})
export class WebhooksModule {}
119 changes: 119 additions & 0 deletions src/modules/webhooks/webhooks.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import {
HttpException,
HttpStatus,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import {
webhooks,
webhooksCreateInput,
webhooksOrderByInput,
webhooksUpdateInput,
webhooksWhereInput,
webhooksWhereUniqueInput,
} from '@prisma/client';
import { Expose } from '../prisma/prisma.interface';
import { PrismaService } from '../prisma/prisma.service';
import { StripeService } from '../stripe/stripe.service';

@Injectable()
export class WebhooksService {
constructor(
private prisma: PrismaService,
private stripeService: StripeService,
) {}

async createWebhook(
groupId: number,
data: Omit<Omit<webhooksCreateInput, 'webhook'>, 'group'>,
): Promise<webhooks> {
return this.prisma.webhooks.create({
data: { ...data, group: { connect: { id: groupId } } },
});
}

async getWebhooks(
groupId: number,
params: {
skip?: number;
take?: number;
cursor?: webhooksWhereUniqueInput;
where?: webhooksWhereInput;
orderBy?: webhooksOrderByInput;
},
): Promise<Expose<webhooks>[]> {
const { skip, take, cursor, where, orderBy } = params;
const webhooks = await this.prisma.webhooks.findMany({
skip,
take,
cursor,
where: { ...where, group: { id: groupId } },
orderBy,
});
return webhooks.map((group) => this.prisma.expose<webhooks>(group));
}

async getWebhook(groupId: number, id: number): Promise<Expose<webhooks>> {
const webhook = await this.prisma.webhooks.findOne({
where: { id },
});
if (!webhook)
throw new HttpException('Webhook not found', HttpStatus.NOT_FOUND);
if (webhook.groupId !== groupId) throw new UnauthorizedException();
return this.prisma.expose<webhooks>(webhook);
}

async updateWebhook(
groupId: number,
id: number,
data: webhooksUpdateInput,
): Promise<Expose<webhooks>> {
const testWebhook = await this.prisma.webhooks.findOne({
where: { id },
});
if (!testWebhook)
throw new HttpException('Webhook not found', HttpStatus.NOT_FOUND);
if (testWebhook.groupId !== groupId) throw new UnauthorizedException();
const webhook = await this.prisma.webhooks.update({
where: { id },
data,
});
return this.prisma.expose<webhooks>(webhook);
}

async replaceWebhook(
groupId: number,
id: number,
data: webhooksCreateInput,
): Promise<Expose<webhooks>> {
const testWebhook = await this.prisma.webhooks.findOne({
where: { id },
});
if (!testWebhook)
throw new HttpException('Webhook not found', HttpStatus.NOT_FOUND);
if (testWebhook.groupId !== groupId) throw new UnauthorizedException();
const webhook = await this.prisma.webhooks.update({
where: { id },
data,
});
return this.prisma.expose<webhooks>(webhook);
}

async deleteWebhook(groupId: number, id: number): Promise<Expose<webhooks>> {
const testWebhook = await this.prisma.webhooks.findOne({
where: { id },
});
if (!testWebhook)
throw new HttpException('Webhook not found', HttpStatus.NOT_FOUND);
if (testWebhook.groupId !== groupId) throw new UnauthorizedException();
const webhook = await this.prisma.webhooks.delete({
where: { id },
});
return this.prisma.expose<webhooks>(webhook);
}

async getWebhookScopes(groupId: number): Promise<Record<string, string>> {
const scopes: Record<string, string> = {};
return scopes;
}
}

0 comments on commit 47869cd

Please sign in to comment.