Skip to content

Commit

Permalink
feat(api): add events
Browse files Browse the repository at this point in the history
  • Loading branch information
agentR47@gmail.com committed Feb 8, 2024
1 parent daa1e2a commit a7e19dd
Show file tree
Hide file tree
Showing 17 changed files with 1,033 additions and 93 deletions.
48 changes: 46 additions & 2 deletions apps/api/src/api-key/service/api-key.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { addHoursToDate } from '../../common/add-hours-to-date'
import { generateApiKey } from '../../common/api-key-generator'
import { toSHA256 } from '../../common/to-sha256'
import { UpdateApiKey } from '../dto/update.api-key/update.api-key'
import { User } from '@prisma/client'
import { ApiKey, EventSource, EventType, User } from '@prisma/client'
import createEvent from '../../common/create-event'

@Injectable()
export class ApiKeyService {
Expand Down Expand Up @@ -42,6 +43,21 @@ export class ApiKeyService {
}
})

createEvent(
{
triggeredBy: user,
entity: apiKey as ApiKey,
type: EventType.API_KEY_ADDED,
source: EventSource.API_KEY,
title: `API key created`,
metadata: {
apiKeyId: apiKey.id,
name: apiKey.name
}
},
this.prisma
)

this.logger.log(`User ${user.id} created API key ${apiKey.id}`)

return {
Expand Down Expand Up @@ -77,18 +93,46 @@ export class ApiKeyService {
}
})

createEvent(
{
triggeredBy: user,
entity: updatedApiKey as ApiKey,
type: EventType.API_KEY_UPDATED,
source: EventSource.API_KEY,
title: `API key updated`,
metadata: {
apiKeyId: updatedApiKey.id,
name: updatedApiKey.name
}
},
this.prisma
)

this.logger.log(`User ${user.id} updated API key ${apiKeyId}`)

return updatedApiKey
}

async deleteApiKey(user: User, apiKeyId: string) {
return this.prisma.apiKey.delete({
const apiKey = await this.prisma.apiKey.delete({
where: {
id: apiKeyId,
userId: user.id
}
})

createEvent(
{
triggeredBy: user,
type: EventType.API_KEY_DELETED,
source: EventSource.API_KEY,
title: `API key deleted`,
metadata: {
name: apiKey.name
}
},
this.prisma
)
}

async getApiKeyById(user: User, apiKeyId: string) {
Expand Down
4 changes: 3 additions & 1 deletion apps/api/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ApiKeyModule } from '../api-key/api-key.module'
import { WorkspaceModule } from '../workspace/workspace.module'
import { WorkspaceRoleModule } from '../workspace-role/workspace-role.module'
import { ApiKeyGuard } from '../auth/guard/api-key/api-key.guard'
import { EventModule } from '../event/event.module'

@Module({
controllers: [AppController],
Expand All @@ -34,7 +35,8 @@ import { ApiKeyGuard } from '../auth/guard/api-key/api-key.guard'
ProjectModule,
EnvironmentModule,
WorkspaceModule,
WorkspaceRoleModule
WorkspaceRoleModule,
EventModule
],
providers: [
{
Expand Down
155 changes: 155 additions & 0 deletions apps/api/src/common/create-event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import {
ApiKey,
Environment,
EventSeverity,
EventTriggerer,
EventType,
EventSource,
PrismaClient,
Project,
Secret,
User,
Workspace,
WorkspaceMember,
WorkspaceRole
} from '@prisma/client'
import { JsonObject } from '@prisma/client/runtime/library'

export default async function createEvent(
data: {
triggerer?: EventTriggerer
severity?: EventSeverity
triggeredBy?: User
entity?:
| Workspace
| Project
| Environment
| WorkspaceRole
| WorkspaceMember
| ApiKey
| Secret
type: EventType
source: EventSource
title: string
description?: string
metadata: JsonObject
},
prisma: PrismaClient
) {
if (data.triggerer !== EventTriggerer.SYSTEM && !data.triggeredBy) {
throw new Error('User must be provided for non-system events')

Check warning on line 40 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L40

Added line #L40 was not covered by tests
}

const baseData = {
triggerer: data.triggerer ? data.triggerer : EventTriggerer.USER,
severity: data.severity ? data.severity : EventSeverity.INFO,
type: data.type,
source: data.source,
title: data.title,
description: data.description,
metadata: data.metadata,
sourceUser: {
connect: {
id: data.triggeredBy.id
}
}
}

switch (data.source) {
case EventSource.WORKSPACE: {
const entity = data.entity as Workspace
await prisma.event.create({

Check warning on line 61 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L59-L61

Added lines #L59 - L61 were not covered by tests
data: {
...baseData,
sourceWorkspace: data.entity
? {

Check warning on line 65 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L65

Added line #L65 was not covered by tests
connect: {
id: entity.id
}
}
: undefined

Check warning on line 70 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L70

Added line #L70 was not covered by tests
}
})
break

Check warning on line 73 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L73

Added line #L73 was not covered by tests
}
case EventSource.PROJECT: {
const entity = data.entity as Project
await prisma.event.create({

Check warning on line 77 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L75-L77

Added lines #L75 - L77 were not covered by tests
data: {
...baseData,
sourceProject: data.entity
? { connect: { id: entity.id } }
: undefined

Check warning on line 82 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L81-L82

Added lines #L81 - L82 were not covered by tests
}
})
break

Check warning on line 85 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L85

Added line #L85 was not covered by tests
}
case EventSource.ENVIRONMENT: {
const entity = data.entity as Environment
await prisma.event.create({

Check warning on line 89 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L87-L89

Added lines #L87 - L89 were not covered by tests
data: {
...baseData,
sourceEnvironment: data.entity
? { connect: { id: entity.id } }
: undefined

Check warning on line 94 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L93-L94

Added lines #L93 - L94 were not covered by tests
}
})
break

Check warning on line 97 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L97

Added line #L97 was not covered by tests
}
case EventSource.WORKSPACE_ROLE: {
const entity = data.entity as WorkspaceRole
await prisma.event.create({
data: {
...baseData,
sourceWorkspaceRole: data.entity
? { connect: { id: entity.id } }
: undefined
}
})
break
}
case EventSource.WORKSPACE_MEMBER: {
const entity = data.entity as WorkspaceMember
await prisma.event.create({

Check warning on line 113 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L111-L113

Added lines #L111 - L113 were not covered by tests
data: {
...baseData,
sourceWorkspaceMembership: data.entity
? { connect: { id: entity.id } }
: undefined

Check warning on line 118 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L117-L118

Added lines #L117 - L118 were not covered by tests
}
})
break

Check warning on line 121 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L121

Added line #L121 was not covered by tests
}
case EventSource.API_KEY: {
const entity = data.entity as ApiKey
await prisma.event.create({
data: {
...baseData,
sourceApiKey: data.entity ? { connect: { id: entity.id } } : undefined
}
})
break
}
case EventSource.SECRET: {
const entity = data.entity as Secret
await prisma.event.create({

Check warning on line 135 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L133-L135

Added lines #L133 - L135 were not covered by tests
data: {
...baseData,
sourceSecret: data.entity ? { connect: { id: entity.id } } : undefined

Check warning on line 138 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L138

Added line #L138 was not covered by tests
}
})
break

Check warning on line 141 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L141

Added line #L141 was not covered by tests
}
case EventSource.USER: {
await prisma.event.create({
data: {
...baseData
}
})
break
}
default: {
throw new Error('Invalid event source')

Check warning on line 152 in apps/api/src/common/create-event.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-event.ts#L151-L152

Added lines #L151 - L152 were not covered by tests
}
}
}
56 changes: 56 additions & 0 deletions apps/api/src/common/get-secret-with-authority.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Authority, PrismaClient, Secret, User } from '@prisma/client'
import { SecretWithProjectAndVersion } from '../secret/secret.types'
import getCollectiveProjectAuthorities from './get-collective-project-authorities'
import { ConflictException, NotFoundException } from '@nestjs/common'

export default async function getSecretWithAuthority(
userId: User['id'],
secretId: Secret['id'],
authority: Authority,
prisma: PrismaClient

Check warning on line 10 in apps/api/src/common/get-secret-with-authority.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/get-secret-with-authority.ts#L10

Added line #L10 was not covered by tests
): Promise<SecretWithProjectAndVersion> {
// Fetch the secret
const secret = await prisma.secret.findUnique({

Check warning on line 13 in apps/api/src/common/get-secret-with-authority.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/get-secret-with-authority.ts#L13

Added line #L13 was not covered by tests
where: {
id: secretId
},
include: {
versions: true,
project: {
include: {
workspace: {
include: {
members: true
}
}
}
}
}
})

if (!secret) {
throw new NotFoundException(`Secret with id ${secretId} not found`)

Check warning on line 32 in apps/api/src/common/get-secret-with-authority.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/get-secret-with-authority.ts#L31-L32

Added lines #L31 - L32 were not covered by tests
}

// Check if the user has the project in their workspace role list
const permittedAuthorities = await getCollectiveProjectAuthorities(

Check warning on line 36 in apps/api/src/common/get-secret-with-authority.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/get-secret-with-authority.ts#L36

Added line #L36 was not covered by tests
userId,
secret.project,
prisma
)

// Check if the user has the required authorities
if (
!permittedAuthorities.has(authority) &&
!permittedAuthorities.has(Authority.WORKSPACE_ADMIN)

Check warning on line 45 in apps/api/src/common/get-secret-with-authority.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/get-secret-with-authority.ts#L43-L45

Added lines #L43 - L45 were not covered by tests
) {
throw new ConflictException(

Check warning on line 47 in apps/api/src/common/get-secret-with-authority.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/get-secret-with-authority.ts#L47

Added line #L47 was not covered by tests
`User ${userId} does not have the required authorities`
)
}

// Remove the workspace from the secret
secret.project.workspace = undefined

Check warning on line 53 in apps/api/src/common/get-secret-with-authority.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/get-secret-with-authority.ts#L53

Added line #L53 was not covered by tests

return secret

Check warning on line 55 in apps/api/src/common/get-secret-with-authority.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/get-secret-with-authority.ts#L55

Added line #L55 was not covered by tests
}
Loading

0 comments on commit a7e19dd

Please sign in to comment.