Skip to content

Commit

Permalink
feat: 🔥 support inline policies API
Browse files Browse the repository at this point in the history
support inline policies API
  • Loading branch information
Tal Rofe committed Jun 7, 2022
1 parent d5e8f7a commit 6f070ce
Show file tree
Hide file tree
Showing 37 changed files with 519 additions and 29 deletions.
42 changes: 23 additions & 19 deletions apps/backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,7 @@ enum PolicyLibrary {
ESLINT
PRETTIER
INFLINT
}

enum LibraryCategory {
LINTER
}

type InlinePolicy {
label String
library PolicyLibrary
category LibraryCategory
configuration Json?
rules Json?
STYLELINT
}

model User {
Expand Down Expand Up @@ -79,17 +68,33 @@ model ClientSecret {
@@unique(fields: [userId, secret], name: "unique_user_secrets")
}

model InlinePolicy {
id String @id @default(auto()) @map("_id") @db.ObjectId
groupId String @db.ObjectId
label String
library PolicyLibrary
configuration Json?
rules Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
}

model Group {
id String @id @default(auto()) @map("_id") @db.ObjectId
userId String @db.ObjectId
label String?
inlinePolicies InlinePolicy[]
policyIDs String[] @db.ObjectId
userId String @db.ObjectId
label String?
policyIDs String[] @db.ObjectId
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
inlinePolicies InlinePolicy[]
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
policies Policy[] @relation(fields: [policyIDs], references: [id])
Expand All @@ -99,13 +104,12 @@ model Group {
model Policy {
id String @id @default(auto()) @map("_id") @db.ObjectId
userId String @db.ObjectId
userId String @db.ObjectId
label String
library PolicyLibrary
category LibraryCategory
configuration Json?
rules Json?
groupsIDs String[] @db.ObjectId
groupsIDs String[] @db.ObjectId
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export class BelongingGroupGuard implements CanActivate {
const userId = user.sub;
const groupId = request.params.group_id as string;

const groupBelongUser = await this.dbGroupService.doesGroupBelongUser(userId, groupId);
const groupBelongsUser = await this.dbGroupService.doesGroupBelongUser(userId, groupId);

return groupBelongUser;
return groupBelongsUser;
}
}
6 changes: 6 additions & 0 deletions apps/backend/src/models/policy-library.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export enum PolicyLibrary {
ESLINT = 'ESLINT',
STYLELINT = 'STYLELINT',
PRETTIER = 'PRETTIER',
INFLINT = 'INFLINT',
}
5 changes: 3 additions & 2 deletions apps/backend/src/modules/database/database.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { Global, Module } from '@nestjs/common';

import { DBClientSecretService } from './client-secret.service';
import { DBGroupService } from './group.service';
import { DBInlinePolicyService } from './inline-policy.service';
import { PrismaService } from './prisma.service';
import { DBUserService } from './user.service';

@Global()
@Module({
providers: [PrismaService, DBUserService, DBClientSecretService, DBGroupService],
exports: [DBUserService, DBClientSecretService, DBGroupService],
providers: [PrismaService, DBUserService, DBClientSecretService, DBGroupService, DBInlinePolicyService],
exports: [DBUserService, DBClientSecretService, DBGroupService, DBInlinePolicyService],
})
export class DatabaseModule {}
2 changes: 1 addition & 1 deletion apps/backend/src/modules/database/group.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class DBGroupService {

public async createGroup(userId: string) {
const createdGroup = await this.prisma.group.create({
data: { userId, inlinePolicies: [], policyIDs: [] },
data: { userId, policyIDs: [] },
select: { id: true },
});

Expand Down
85 changes: 85 additions & 0 deletions apps/backend/src/modules/database/inline-policy.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';

import { PolicyLibrary } from '@/models/policy-library';

import { PrismaService } from './prisma.service';

@Injectable()
export class DBInlinePolicyService {
constructor(private prisma: PrismaService) {}

public async createInlinePolicy(groupId: string, label: string, library: PolicyLibrary) {
const createdInlinePolicy = await this.prisma.inlinePolicy.create({
data: { groupId, label, library },
select: { id: true },
});

return createdInlinePolicy.id;
}

public async deleteInlinePolicy(inlinePolicyId: string) {
await this.prisma.inlinePolicy.delete({ where: { id: inlinePolicyId } });
}

public async updateConfiguration(inlinePolicyId: string, configuration: Record<string, unknown>) {
await this.prisma.inlinePolicy.update({
where: { id: inlinePolicyId },
data: { configuration: configuration as Prisma.JsonObject },
});
}

public async doesInlinePolicyBelongUser(inlinePolicyId: string, userId: string) {
const inlinePolicyDB = await this.prisma.inlinePolicy.findFirst({
where: { id: inlinePolicyId, group: { userId } },
});

return inlinePolicyDB !== null;
}

public async addRule(inlinePolicyId: string, rule: Record<string, unknown>) {
const inlinePolicyDB = await this.prisma.inlinePolicy.findFirst({
where: { id: inlinePolicyId },
select: { rules: true },
rejectOnNotFound: true,
});

let newInlinePolicyRules: Prisma.JsonObject;

if (!inlinePolicyDB.rules) {
newInlinePolicyRules = rule as Prisma.JsonObject;
} else {
newInlinePolicyRules = {
...(inlinePolicyDB.rules as Prisma.JsonObject),
...rule,
} as Prisma.JsonObject;
}

await this.prisma.inlinePolicy.update({
where: { id: inlinePolicyId },
data: { rules: newInlinePolicyRules },
});
}

public async removeRule(inlinePolicyId: string, ruleName: string) {
const inlinePolicyDB = await this.prisma.inlinePolicy.findFirst({
where: { id: inlinePolicyId },
select: { rules: true },
rejectOnNotFound: true,
});

if (!inlinePolicyDB.rules) {
return;
}

const rulesWithoutRule = {
...(inlinePolicyDB.rules as Prisma.JsonObject),
[ruleName]: undefined,
};

await this.prisma.inlinePolicy.update({
where: { id: inlinePolicyId },
data: { rules: rulesWithoutRule },
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { Controller, Delete, HttpCode, HttpStatus, Logger, Param, UseGuards } fr
import { CommandBus } from '@nestjs/cqrs';

import { CurrentUserId } from '@/decorators/current-user-id.decorator';
import { BelongingGroupGuard } from '@/guards/belonging-group.guard';

import Routes from './groups.routes';
import { BelongingGroupGuard } from './guards/belonging-group.guard';
import { DeleteContract } from './commands/contracts/delete.contract';

@Controller(Routes.CONTROLLER)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { Body, Controller, HttpCode, HttpStatus, Logger, Param, Patch, UseGuards
import { CommandBus } from '@nestjs/cqrs';

import { CurrentUserId } from '@/decorators/current-user-id.decorator';
import { BelongingGroupGuard } from '@/guards/belonging-group.guard';

import Routes from './groups.routes';
import { EditLabelDto } from './classes/edit-label.dto';
import { EditLabelContract } from './commands/contracts/edit-label.contract';
import { BelongingGroupGuard } from './guards/belonging-group.guard';

@Controller(Routes.CONTROLLER)
export class EditLabelController {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Module } from '@nestjs/common';

import { BelongingGroupGuard } from '@/guards/belonging-group.guard';

import { CommandHandlers } from './commands/handlers';
import { CreateController } from './create.controller';
import { DeleteController } from './delete.controller';
import { EditLabelController } from './edit-label.controller';
import { BelongingGroupGuard } from './guards/belonging-group.guard';
import { QueryHandlers } from './queries/handlers';

@Module({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Body, Controller, Logger, Param, Post, UseGuards } from '@nestjs/common';
import { CommandBus } from '@nestjs/cqrs';

import Routes from './inline-policies.routes';
import { BelongingInlinePolicyGuard } from './guards/belonging-inline-policy.guard';
import { AddRuleDto } from './classes/add-rule.dto';
import { AddRuleContract } from './commands/contracts/add-rule.contract';

@Controller(Routes.CONTROLLER)
export class AddRuleController {
private readonly logger = new Logger(AddRuleController.name);

constructor(private readonly commandBus: CommandBus) {}

@UseGuards(BelongingInlinePolicyGuard)
@Post(Routes.ADD_RULE)
public async addRule(
@Param('policy_id') policyId: string,
@Body() addRuleDto: AddRuleDto,
): Promise<void> {
this.logger.log(`Will try to add rule for an inline policy with an Id: "${policyId}"`);

await this.commandBus.execute<AddRuleContract, void>(new AddRuleContract(policyId, addRuleDto.rule));

this.logger.log(`Successfully added a rule for an inline policy Id: "${policyId}"`);
}

@UseGuards(BelongingInlinePolicyGuard)
@Post(Routes.EDIT_RULE)
public async editRule(
@Param('policy_id') policyId: string,
@Body() editRuleDto: AddRuleDto,
): Promise<void> {
this.logger.log(`Will try to edit rule for an inline policy with an Id: "${policyId}"`);

await this.commandBus.execute<AddRuleContract, void>(new AddRuleContract(policyId, editRuleDto.rule));

this.logger.log(`Successfully edited a rule for an inline policy Id: "${policyId}"`);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { IsJSON } from 'class-validator';

export class AddRuleDto {
@IsJSON()
readonly rule!: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { IsEnum, IsString, MinLength } from 'class-validator';

import { PolicyLibrary } from '@/models/policy-library';

export class CreateInlineDto {
@IsString()
@MinLength(1)
readonly label!: string;

@IsEnum(PolicyLibrary)
readonly library!: PolicyLibrary;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { IsString, MinLength } from 'class-validator';

export class RemoveRuleDto {
@IsString()
@MinLength(1)
readonly ruleName!: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { IsJSON } from 'class-validator';

export class UpdateConfigurationDto {
@IsJSON()
readonly configuration!: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export class AddRuleContract {
constructor(public readonly policyId: string, public readonly rule: string) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export class DeleteInlineContract {
constructor(public readonly policyId: string) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export class RemoveRuleContract {
constructor(public readonly policyId: string, public readonly ruleName: string) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export class UpdateConfigurationContract {
constructor(public readonly policyId: string, public readonly configuration: string) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';

import { DBInlinePolicyService } from '@/modules/database/inline-policy.service';

import { AddRuleContract } from '../contracts/add-rule.contract';

@CommandHandler(AddRuleContract)
export class AddRuleHandler implements ICommandHandler<AddRuleContract> {
constructor(private readonly dbInlinePolicyService: DBInlinePolicyService) {}

async execute(contract: AddRuleContract) {
const parsedRule = JSON.parse(contract.rule);

await this.dbInlinePolicyService.addRule(contract.policyId, parsedRule);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';

import { DBInlinePolicyService } from '@/modules/database/inline-policy.service';

import { DeleteInlineContract } from '../contracts/delete-inline.contract';

@CommandHandler(DeleteInlineContract)
export class DeleteInlineHandler implements ICommandHandler<DeleteInlineContract> {
constructor(private readonly dbInlinePolicyService: DBInlinePolicyService) {}

async execute(contract: DeleteInlineContract) {
await this.dbInlinePolicyService.deleteInlinePolicy(contract.policyId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { AddRuleHandler } from './add-rule.handler';
import { DeleteInlineHandler } from './delete-inline.handler';
import { RemoveRuleHandler } from './remove-rule.handler';
import { UpdateConfigurationHandler } from './update-configuration.handler';

export const CommandHandlers = [
DeleteInlineHandler,
UpdateConfigurationHandler,
AddRuleHandler,
RemoveRuleHandler,
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';

import { DBInlinePolicyService } from '@/modules/database/inline-policy.service';

import { RemoveRuleContract } from '../contracts/remove-rule.contract';

@CommandHandler(RemoveRuleContract)
export class RemoveRuleHandler implements ICommandHandler<RemoveRuleContract> {
constructor(private readonly dbInlinePolicyService: DBInlinePolicyService) {}

async execute(contract: RemoveRuleContract) {
await this.dbInlinePolicyService.removeRule(contract.policyId, contract.ruleName);
}
}
Loading

0 comments on commit 6f070ce

Please sign in to comment.