Skip to content

Commit

Permalink
feat(cli): Add project command (#451)
Browse files Browse the repository at this point in the history
  • Loading branch information
rajdip-b authored Sep 21, 2024
1 parent 6a1c091 commit 70448e1
Show file tree
Hide file tree
Showing 24 changed files with 660 additions and 75 deletions.
2 changes: 1 addition & 1 deletion apps/api/src/project/controller/project.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export class ProjectController {
async syncFork(
@CurrentUser() user: User,
@Param('projectSlug') projectSlug: Project['slug'],
@Param('hardSync') hardSync: boolean = false
@Query('hardSync') hardSync: boolean = false
) {
return await this.service.syncFork(user, projectSlug, hardSync)
}
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/project/dto/fork.project/fork.project.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Workspace } from '@prisma/client'
import { IsOptional, IsString } from 'class-validator'
import { IsBoolean, IsOptional, IsString } from 'class-validator'

export class ForkProject {
@IsString()
Expand All @@ -10,7 +10,7 @@ export class ForkProject {
@IsOptional()
name?: string

@IsString()
@IsBoolean()
@IsOptional()
storePrivateKey?: boolean
}
28 changes: 15 additions & 13 deletions apps/api/src/workspace-role/service/workspace-role.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,20 +110,22 @@ export class WorkspaceRoleService {
})
)

// Create the project associations
const projectSlugToIdMap = await this.getProjectSlugToIdMap(
dto.projectSlugs
)

if (dto.projectSlugs && dto.projectSlugs.length > 0) {
op.push(
this.prisma.projectWorkspaceRoleAssociation.createMany({
data: dto.projectSlugs.map((projectSlug) => ({
roleId: workspaceRoleId,
projectId: projectSlugToIdMap.get(projectSlug)
}))
})
if (dto.projectSlugs) {
// Create the project associations
const projectSlugToIdMap = await this.getProjectSlugToIdMap(
dto.projectSlugs
)

if (dto.projectSlugs && dto.projectSlugs.length > 0) {
op.push(
this.prisma.projectWorkspaceRoleAssociation.createMany({
data: dto.projectSlugs.map((projectSlug) => ({
roleId: workspaceRoleId,
projectId: projectSlugToIdMap.get(projectSlug)
}))
})
)
}
}

const workspaceRole = (await this.prisma.$transaction(op))[0]
Expand Down
4 changes: 2 additions & 2 deletions apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
"name": "cli",
"version": "1.0.0",
"description": "CLI for keyshade",
"main": "index.ts",
"main": "dist/src/index.js",
"private": false,
"type": "module",
"scripts": {
"build": "tsc && tsc-alias",
"start": "node dist/src/index.js",
"dev": "pnpm build && node dist/index.js",
"dev": "pnpm build && node dist/src/index.js",
"lint": "eslint \"src/**/*.ts\" --fix"
},
"keywords": [],
Expand Down
19 changes: 12 additions & 7 deletions apps/cli/src/commands/base.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type {
import { fetchProfileConfig } from '@/util/configuration'
import { Logger } from '@/util/logger'
import { getDefaultProfile } from '@/util/profile'
import type { Command } from 'commander'
import { Option, type Command } from 'commander'
import ControllerInstance from '@/util/controller-instance'

/**
Expand Down Expand Up @@ -55,13 +55,18 @@ export default abstract class BaseCommand {
}
})

this.getOptions().forEach((option) =>
command.option(
this.getOptions().forEach((option) => {
const newOption: Option = new Option(
`${option.short}, ${option.long}`,
option.description,
option.defaultValue
)
)
option.description
).default(option.defaultValue)

option.choices &&
option.choices.length > 0 &&
newOption.choices(option.choices)

command.addOption(newOption)
})
this.getArguments().forEach((argument) =>
command.argument(argument.name, argument.description)
)
Expand Down
34 changes: 34 additions & 0 deletions apps/cli/src/commands/project.command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import BaseCommand from './base.command'
import CreateProject from './project/create.project'
import DeleteProject from './project/delete.project'
import ForkProject from './project/fork.project'
import GetProject from './project/get.project'
import ListProjectForks from './project/list-forks.project'
import ListProject from './project/list.project'
import SyncProject from './project/sync.project'
import UnlinkProject from './project/unlink.project'
import UpdateProject from './project/update.project'

export default class ProjectCommand extends BaseCommand {
getName(): string {
return 'project'
}

getDescription(): string {
return 'Manage projects of a workspace'
}

getSubCommands(): BaseCommand[] {
return [
new CreateProject(),
new DeleteProject(),
new ForkProject(),
new GetProject(),
new ListProjectForks(),
new ListProject(),
new SyncProject(),
new UnlinkProject(),
new UpdateProject()
]
}
}
103 changes: 103 additions & 0 deletions apps/cli/src/commands/project/create.project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import type {
CommandActionData,
CommandArgument,
CommandOption
} from '@/types/command/command.types'
import BaseCommand from '../base.command'
import { text } from '@clack/prompts'
import ControllerInstance from '@/util/controller-instance'
import { Logger } from '@/util/logger'

export default class CreateProject extends BaseCommand {
getName(): string {
return 'create'
}

getDescription(): string {
return 'Creates a project'
}

getArguments(): CommandArgument[] {
return [
{
name: '<Workspace Slug>',
description:
'Slug of the workspace under which you want to create the project'
}
]
}

getOptions(): CommandOption[] {
return [
{
short: '-n',
long: '--name <string>',
description: 'Name of the project'
},
{
short: '-d',
long: '--description <string>',
description: 'Description of the project. Defaults to project name'
},
{
short: '-k',
long: '--store-private-key',
description: 'Store the private key in the project. Defaults to true',
defaultValue: true
},
{
short: '-a',
long: '--access-level <string>',
description: 'Access level of the project. Defaults to PRIVATE.',
defaultValue: 'PRIVATE',
choices: ['GLOBAL', 'PRIVATE', 'INTERNAL']
}
]
}

async action({ args, options }: CommandActionData): Promise<void> {
const [workspaceSlug] = args

const parsedData = await this.parseOptions(options)

const { data, error, success } =
await ControllerInstance.getInstance().projectController.createProject(
{
workspaceSlug,
...parsedData
},
this.headers
)

if (success) {
Logger.info(`Project ${data.name} (${data.slug}) created successfully!`)
Logger.info(`Created at ${data.createdAt}`)
Logger.info(`Updated at ${data.updatedAt}`)
} else {
Logger.error(`Failed to create project: ${error.message}`)
}
}

async parseOptions(options: CommandActionData['options']): Promise<{
name: string
description?: string
storePrivateKey: boolean
accessLevel: string
}> {
let { name, description } = options
const { storePrivateKey, accessLevel } = options

if (!name) {
name = await text({
message: 'Enter the name of the Project',
placeholder: 'My Project'
})
}

if (!description) {
description = name
}

return { name, description, storePrivateKey, accessLevel }
}
}
44 changes: 44 additions & 0 deletions apps/cli/src/commands/project/delete.project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type {
CommandActionData,
CommandArgument
} from '@/types/command/command.types'
import BaseCommand from '../base.command'
import { Logger } from '@/util/logger'
import ControllerInstance from '@/util/controller-instance'

export default class DeleteProject extends BaseCommand {
getName(): string {
return 'delete'
}

getDescription(): string {
return 'Deletes a project'
}

getArguments(): CommandArgument[] {
return [
{
name: '<Project Slug>',
description: 'Slug of the project that you want to delete.'
}
]
}

async action({ args }: CommandActionData): Promise<void> {
const [projectSlug] = args

const { error, success } =
await ControllerInstance.getInstance().projectController.deleteProject(
{
projectSlug
},
this.headers
)

if (success) {
Logger.info(`Project ${projectSlug} deleted successfully!`)
} else {
Logger.error(`Failed to delete project: ${error.message}`)
}
}
}
71 changes: 71 additions & 0 deletions apps/cli/src/commands/project/fork.project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import type {
CommandActionData,
CommandArgument,
CommandOption
} from '@/types/command/command.types'
import BaseCommand from '../base.command'
import ControllerInstance from '@/util/controller-instance'
import { Logger } from '@/util/logger'

export default class ForkProject extends BaseCommand {
getName(): string {
return 'fork'
}

getDescription(): string {
return 'Forks a project'
}

getArguments(): CommandArgument[] {
return [
{
name: '<Project Slug>',
description: 'Slug of the project which you want to fork.'
}
]
}

getOptions(): CommandOption[] {
return [
{
short: '-n',
long: '--name <Workspace Name>',
description: 'Name of the workspace.'
},
{
short: '-k',
long: '--store-private-key <boolean>',
description: 'Store the private key in the project. Defaults to true',
defaultValue: true
},
{
short: '-w',
long: '--workspace <string>',
description: 'Workspace slug to fork the project in'
}
]
}

async action({ options, args }: CommandActionData): Promise<void> {
const [projectSlug] = args

console.log(options)

const { data, error, success } =
await ControllerInstance.getInstance().projectController.forkProject(
{
projectSlug,
...options
},
this.headers
)

if (success) {
Logger.info(`Project ${data.name} (${data.slug}) forked successfully!`)
Logger.info(`Created at ${data.createdAt}`)
Logger.info(`Updated at ${data.updatedAt}`)
} else {
Logger.error(`Failed to fork project: ${error.message}`)
}
}
}
Loading

0 comments on commit 70448e1

Please sign in to comment.