Skip to content

Commit

Permalink
Merge branch 'develop' into Api-client_invitationList
Browse files Browse the repository at this point in the history
  • Loading branch information
Souvik9205 authored Jan 11, 2025
2 parents c74d71b + e64a738 commit 561dbd7
Show file tree
Hide file tree
Showing 30 changed files with 8,260 additions and 394 deletions.
2,578 changes: 2,466 additions & 112 deletions apps/api/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"@nestjs/swagger": "^7.3.0",
"@nestjs/throttler": "^6.2.1",
"@nestjs/websockets": "^10.3.7",
"@slack/bolt": "^3.22.0",
"@react-email/components": "^0.0.25",
"@react-email/preview": "0.0.11",
"@react-email/render": "^1.0.1",
Expand Down
3,573 changes: 3,573 additions & 0 deletions apps/api/pnpm-lock.yaml

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions apps/api/src/integration/integration.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ export interface DiscordIntegrationMetadata extends IntegrationMetadata {
webhookUrl: string
}

export interface SlackIntegrationMetadata extends IntegrationMetadata {
botToken: string
signingSecret: string
channelId: string
}

export interface IntegrationWithWorkspace extends Integration {
workspace: Workspace
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { IntegrationType } from '@prisma/client'
import { BaseIntegration } from '../base.integration'
import { DiscordIntegration } from '../discord/discord.integration'
import { InternalServerErrorException } from '@nestjs/common'

import { SlackIntegration } from '../slack/slack.integration'
/**
* Factory class to create integrations. This class will be called to create an integration,
* based on the integration type. This has only a single factory method. You will need to
Expand All @@ -20,6 +20,8 @@ export default class IntegrationFactory {
switch (integrationType) {
case IntegrationType.DISCORD:
return new DiscordIntegration()
case IntegrationType.SLACK:
return new SlackIntegration()
default:
throw new InternalServerErrorException('Integration type not found')
}
Expand Down
30 changes: 30 additions & 0 deletions apps/api/src/integration/plugins/slack/slack.integration.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { IntegrationType } from '@prisma/client'
import { SlackIntegration } from './slack.integration'

describe('Slack Integration Test', () => {
let integration: SlackIntegration

beforeEach(() => {
integration = new SlackIntegration()
})

it('should generate slack integration', () => {
expect(integration).toBeDefined()
expect(integration.integrationType).toBe(IntegrationType.SLACK)
})

it('should have the correct permitted events', () => {
const events = integration.getPermittedEvents()
expect(events).toBeDefined()
expect(events.size).toBe(26)
})

it('should have the correct required metadata parameters', () => {
const metadata = integration.getRequiredMetadataParameters()
expect(metadata).toBeDefined()
expect(metadata.size).toBe(3)
expect(metadata.has('botToken')).toBe(true)
expect(metadata.has('signingSecret')).toBe(true)
expect(metadata.has('channelId')).toBe(true)
})
})
120 changes: 120 additions & 0 deletions apps/api/src/integration/plugins/slack/slack.integration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { EventType, IntegrationType } from '@prisma/client'
import {
SlackIntegrationMetadata,
IntegrationEventData
} from '../../integration.types'
import { App } from '@slack/bolt'
import { BaseIntegration } from '../base.integration'
import { Logger } from '@nestjs/common'

export class SlackIntegration extends BaseIntegration {
private readonly logger = new Logger('SlackIntegration')
private app: App
constructor() {
super(IntegrationType.SLACK)
}

public getPermittedEvents(): Set<EventType> {
return new Set([
EventType.INTEGRATION_ADDED,
EventType.INTEGRATION_UPDATED,
EventType.INTEGRATION_DELETED,
EventType.INVITED_TO_WORKSPACE,
EventType.REMOVED_FROM_WORKSPACE,
EventType.ACCEPTED_INVITATION,
EventType.DECLINED_INVITATION,
EventType.CANCELLED_INVITATION,
EventType.LEFT_WORKSPACE,
EventType.WORKSPACE_UPDATED,
EventType.WORKSPACE_CREATED,
EventType.WORKSPACE_ROLE_CREATED,
EventType.WORKSPACE_ROLE_UPDATED,
EventType.WORKSPACE_ROLE_DELETED,
EventType.PROJECT_CREATED,
EventType.PROJECT_UPDATED,
EventType.PROJECT_DELETED,
EventType.SECRET_UPDATED,
EventType.SECRET_DELETED,
EventType.SECRET_ADDED,
EventType.VARIABLE_UPDATED,
EventType.VARIABLE_DELETED,
EventType.VARIABLE_ADDED,
EventType.ENVIRONMENT_UPDATED,
EventType.ENVIRONMENT_DELETED,
EventType.ENVIRONMENT_ADDED,
EventType.INTEGRATION_ADDED,
EventType.INTEGRATION_UPDATED,
EventType.INTEGRATION_DELETED
])
}

public getRequiredMetadataParameters(): Set<string> {
return new Set(['botToken', 'signingSecret', 'channelId'])
}

async emitEvent(
data: IntegrationEventData,
metadata: SlackIntegrationMetadata
): Promise<void> {
this.logger.log(`Emitting event to Slack: ${data.title}`)
try {
if (!this.app) {
this.app = new App({
token: metadata.botToken,
signingSecret: metadata.signingSecret
})
}
const block = [
{
type: 'header',
text: {
type: 'plain_text',
text: 'Update occurred on keyshade',
emoji: true
}
},
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*${data.title ?? 'No title provided'}*\n${data.description ?? 'No description provided'}`
}
},
{
type: 'divider'
},
{
type: 'section',
fields: [
{
type: 'mrkdwn',
text: `*Event:*\n${data.title}`
},
{
type: 'mrkdwn',
text: `*Source:*\n${data.source}`
}
]
},
{
type: 'context',
elements: [
{
type: 'mrkdwn',
text: '<https://keyshade.xyz|View in Keyshade>'
}
]
}
]
await this.app.client.chat.postMessage({
channel: metadata.channelId,
blocks: block,
text: data.title
})
} catch (error) {
this.logger.error(`Failed to emit event to Slack: ${error.message}`)
console.error(error)
throw error
}
}
}
16 changes: 11 additions & 5 deletions apps/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,36 @@

## 2.0.1

### Minor Changes
### Patches

- Fixed invalid import issue

## 2.0.2

### Minor Changes
### Patches

- Added `keyshade` command

## 2.0.3

### Minor Changes
### Patches

- Updated build scripts

## 2.0.4

### Minor Changes
### Patches

- Fixed binary path in package.json

## 2.1.0

### Major Changes
### Minor Changes

- Modified a lot of commands to use more options rather than arguments

## 2.2.0

### Minor Changes

- `project create` command now outputs public key, private key and access level upon success.
4 changes: 2 additions & 2 deletions apps/cli/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@keyshade/cli",
"version": "2.1.0",
"version": "2.2.0",
"description": "CLI for keyshade",
"main": "dist/index.cjs",
"module": "dist/index.esm.js",
Expand Down
3 changes: 3 additions & 0 deletions apps/cli/src/commands/project/create.project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ export default class CreateProject extends BaseCommand {
Logger.info(`Project ${data.name} (${data.slug}) created successfully!`)
Logger.info(`Created at ${data.createdAt}`)
Logger.info(`Updated at ${data.updatedAt}`)
Logger.info(`Public key: ${data.publicKey}`)
Logger.info(`Private key: ${data.privateKey}`)
Logger.info(`Access level: ${data.accessLevel}`)
} else {
Logger.error(`Failed to create project: ${error.message}`)
}
Expand Down
3 changes: 2 additions & 1 deletion apps/platform/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module.exports = {
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-return': 'off'
'@typescript-eslint/no-unsafe-return': 'off',
'no-nested-ternary': 'off'
}
}
2 changes: 2 additions & 0 deletions apps/platform/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
},
"dependencies": {
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-alert-dialog": "^1.1.4",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-collapsible": "^1.1.2",
"@radix-ui/react-context-menu": "^2.1.5",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-direction": "^1.0.1",
Expand Down
6 changes: 4 additions & 2 deletions apps/platform/public/svg/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import ThreeDotOptionSVG from './3dotOption.svg'
import AddSVG from './add.svg'
import LoadingSVG from './loading.svg'
import MessageSVG from './message.svg'
import VectorSVG from './Vector.svg'
import VectorSVG from './vector.svg'
import ErrorSVG from './Error.svg'
import TrashSVG from './trash.svg'

export {
DropdownSVG,
Expand All @@ -23,5 +24,6 @@ export {
LoadingSVG,
MessageSVG,
VectorSVG,
ErrorSVG
ErrorSVG,
TrashSVG
}
6 changes: 6 additions & 0 deletions apps/platform/public/svg/shared/trash.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
11 changes: 9 additions & 2 deletions apps/platform/src/app/(main)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ import {
} from '@/components/ui/dialog'
import ControllerInstance from '@/lib/controller-instance'
import { Textarea } from '@/components/ui/textarea'
import ProjectScreenLoader from '@/components/ui/project-screen-loader'

export default function Index(): JSX.Element {
const [isSheetOpen, setIsSheetOpen] = useState<boolean>(false)
const [isProjectEmpty, setIsProjectEmpty] = useState<boolean>(true)
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(false)
const [loading, setLoading] = useState<boolean>(false)

// Projects to be displayed in the dashboard
const [projects, setProjects] = useState<ProjectWithCount[]>([])
Expand Down Expand Up @@ -78,6 +80,8 @@ export default function Index(): JSX.Element {
// under that workspace and display it in the dashboard.
useEffect(() => {
async function getAllProjects() {
setLoading(true)

if (currentWorkspace) {
const { success, error, data } =
await ControllerInstance.getInstance().projectController.getAllProjects(
Expand All @@ -92,6 +96,8 @@ export default function Index(): JSX.Element {
console.error(error)
}
}

setLoading(false)
}

getAllProjects()
Expand Down Expand Up @@ -147,7 +153,6 @@ export default function Index(): JSX.Element {
<DialogTrigger>
{isProjectEmpty ? null : (
<Button onClick={toggleDialog}>
{' '}
<AddSVG /> Create a new Project
</Button>
)}
Expand Down Expand Up @@ -326,7 +331,9 @@ export default function Index(): JSX.Element {
</Dialog>
</div>

{!isProjectEmpty ? (
{loading ? (
<ProjectScreenLoader />
) : !isProjectEmpty ? (
<div className="grid grid-cols-1 gap-5 overflow-y-scroll scroll-smooth p-2 md:grid-cols-2 xl:grid-cols-3">
{projects.map((project: GetAllProjectsResponse['items'][number]) => {
return (
Expand Down
Loading

0 comments on commit 561dbd7

Please sign in to comment.