7f1e69740a
First PR to implement application tokens - add new application role in twenty-server - move duplicated constants and types to twenty-shared - will add role configuration utils into twenty-sdk in another PR
170 lines
4.7 KiB
TypeScript
170 lines
4.7 KiB
TypeScript
import chalk from 'chalk';
|
|
import * as fs from 'fs-extra';
|
|
import inquirer from 'inquirer';
|
|
import { join } from 'path';
|
|
import camelcase from 'lodash.camelcase';
|
|
import { CURRENT_EXECUTION_DIRECTORY } from '@/cli/constants/current-execution-directory';
|
|
import { getObjectDecoratedClass } from '@/cli/utils/get-object-decorated-class';
|
|
import { getFunctionBaseFile } from '@/cli/utils/get-function-base-file';
|
|
import { convertToLabel } from '@/cli/utils/convert-to-label';
|
|
|
|
export enum SyncableEntity {
|
|
AGENT = 'agent',
|
|
OBJECT = 'object',
|
|
FUNCTION = 'function',
|
|
}
|
|
|
|
export const isSyncableEntity = (value: string): value is SyncableEntity => {
|
|
return Object.values(SyncableEntity).includes(value as SyncableEntity);
|
|
};
|
|
|
|
export class AppAddCommand {
|
|
async execute(entityType?: SyncableEntity, path?: string): Promise<void> {
|
|
try {
|
|
const appPath = join(CURRENT_EXECUTION_DIRECTORY, path ?? '');
|
|
|
|
await fs.ensureDir(appPath);
|
|
|
|
const entity = entityType ?? (await this.getEntity());
|
|
|
|
if (entity === SyncableEntity.OBJECT) {
|
|
const entityData = await this.getObjectData();
|
|
|
|
const name = entityData.nameSingular;
|
|
|
|
const objectFileName = `${camelcase(name)}.ts`;
|
|
|
|
const decoratedObject = getObjectDecoratedClass({
|
|
data: entityData,
|
|
name,
|
|
});
|
|
|
|
await fs.writeFile(join(appPath, objectFileName), decoratedObject);
|
|
|
|
return;
|
|
}
|
|
|
|
if (entity === SyncableEntity.FUNCTION) {
|
|
const entityName = await this.getEntityName(entity);
|
|
|
|
const objectFileName = `${camelcase(entityName)}.ts`;
|
|
|
|
const decoratedServerlessFunction = getFunctionBaseFile({
|
|
name: entityName,
|
|
});
|
|
|
|
await fs.writeFile(
|
|
join(appPath, objectFileName),
|
|
decoratedServerlessFunction,
|
|
);
|
|
|
|
return;
|
|
}
|
|
} catch (error) {
|
|
console.error(
|
|
chalk.red(`Add new entity failed:`),
|
|
error instanceof Error ? error.message : error,
|
|
);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
private async getEntity() {
|
|
const { entity } = await inquirer.prompt<{ entity: SyncableEntity }>([
|
|
{
|
|
type: 'select',
|
|
name: 'entity',
|
|
message: `What entity do you want to create?`,
|
|
default: '',
|
|
choices: [SyncableEntity.FUNCTION, SyncableEntity.OBJECT],
|
|
},
|
|
]);
|
|
|
|
return entity;
|
|
}
|
|
|
|
private async getEntityName(entity: SyncableEntity) {
|
|
const { name } = await inquirer.prompt<{ name: string }>([
|
|
{
|
|
type: 'input',
|
|
name: 'name',
|
|
message: `Enter a name for your new ${entity}:`,
|
|
default: '',
|
|
validate: (input) => {
|
|
if (input.length === 0) {
|
|
return `${entity} name is required`;
|
|
}
|
|
|
|
if (!/^[a-z0-9-]+$/.test(input)) {
|
|
return 'Name must contain only lowercase letters, numbers, and hyphens';
|
|
}
|
|
|
|
return true;
|
|
},
|
|
},
|
|
]);
|
|
|
|
return name;
|
|
}
|
|
|
|
private async getObjectData() {
|
|
return inquirer.prompt([
|
|
{
|
|
type: 'input',
|
|
name: 'nameSingular',
|
|
message: 'Enter a name singular for your object (eg: company):',
|
|
default: '',
|
|
validate: (input: string) => {
|
|
if (!input || input.trim().length === 0) {
|
|
return 'Please enter a non empty string';
|
|
}
|
|
return true;
|
|
},
|
|
},
|
|
{
|
|
type: 'input',
|
|
name: 'namePlural',
|
|
message: 'Enter a name plural for your object (eg: companies):',
|
|
default: '',
|
|
validate: (input: string, answers?: any) => {
|
|
if (input.trim() === answers?.nameSingular.trim()) {
|
|
return 'Name plural must be different from name singular';
|
|
}
|
|
if (!input || input.trim().length === 0) {
|
|
return 'Please enter a non empty string';
|
|
}
|
|
return true;
|
|
},
|
|
},
|
|
{
|
|
type: 'input',
|
|
name: 'labelSingular',
|
|
message: 'Enter a label singular for your object:',
|
|
default: (answers: any) => {
|
|
return convertToLabel(answers.nameSingular);
|
|
},
|
|
validate: (input: string) => {
|
|
if (!input || input.trim().length === 0) {
|
|
return 'Please enter a non empty string';
|
|
}
|
|
return true;
|
|
},
|
|
},
|
|
{
|
|
type: 'input',
|
|
name: 'labelPlural',
|
|
message: 'Enter a label plural for your object:',
|
|
default: (answers: any) => {
|
|
return convertToLabel(answers.namePlural);
|
|
},
|
|
validate: (input: string) => {
|
|
if (!input || input.trim().length === 0) {
|
|
return 'Please enter a non empty string';
|
|
}
|
|
return true;
|
|
},
|
|
},
|
|
]);
|
|
}
|
|
}
|