1895 extensibility v1 application tokens (#16365)

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
This commit is contained in:
martmull
2025-12-08 10:42:46 +01:00
committed by GitHub
parent 1f9a6b067a
commit 7f1e69740a
188 changed files with 977 additions and 457 deletions
@@ -1,4 +1,4 @@
import { convertToLabel } from '../convert-to-label';
import { convertToLabel } from '@/cli/utils/convert-to-label';
describe('convertToLabel', () => {
it('should convert to label', () => {
@@ -1,4 +1,4 @@
import { getFunctionBaseFile } from '../get-function-base-file';
import { getFunctionBaseFile } from '@/cli/utils/get-function-base-file';
describe('getFunctionBaseFile', () => {
it('should render proper file', () => {
@@ -1,4 +1,4 @@
import { getObjectDecoratedClass } from '../get-object-decorated-class';
import { getObjectDecoratedClass } from '@/cli/utils/get-object-decorated-class';
describe('getObjectDecoratedClass', () => {
it('should return proper object file', () => {
@@ -1,7 +1,7 @@
import { ensureDirSync, writeFileSync, removeSync } from 'fs-extra';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
import { loadManifest } from '../load-manifest';
import { loadManifest } from '@/cli/utils/load-manifest';
import { v4 } from 'uuid';
const write = (root: string, file: string, content: string) => {
@@ -74,13 +74,13 @@ declare module 'twenty-sdk' {
icon?: string;
};
export const ObjectMetadata = (_: ObjectMetadataOptions): ClassDecorator => {
export const Object = (_: ObjectMetadataOptions): ClassDecorator => {
return () => {};
};
export class BaseObjectMetadata {}
export enum FieldMetadataType {
export enum FieldType {
TEXT = 'TEXT',
FULL_NAME = 'FULL_NAME',
ADDRESS = 'ADDRESS',
@@ -88,7 +88,7 @@ declare module 'twenty-sdk' {
DATE_TIME = 'DATE_TIME',
}
export const FieldMetadata: (_: any) => PropertyDecorator;
export const Field: (_: any) => PropertyDecorator;
}
`;
@@ -125,10 +125,9 @@ export const config: FunctionConfig = {
};`;
const objectMock = `import {
ObjectMetadata,
BaseObjectMetadata,
FieldMetadata,
FieldMetadataType
Object,
Field,
FieldType
} from 'twenty-sdk';
enum PostCardStatus {
@@ -138,7 +137,7 @@ enum PostCardStatus {
RETURNED = 'RETURNED',
}
@ObjectMetadata({
@Object({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
@@ -147,32 +146,32 @@ enum PostCardStatus {
description: ' A post card object',
icon: 'IconMail',
})
export class PostCard extends BaseObjectMetadata {
@FieldMetadata({
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldMetadataType.TEXT,
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
})
content: string;
@FieldMetadata({
@Field({
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
type: FieldMetadataType.FULL_NAME,
type: FieldType.FULL_NAME,
label: 'Recipient name',
})
recipientName: string;
@FieldMetadata({
@Field({
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
type: FieldMetadataType.ADDRESS,
type: FieldType.ADDRESS,
label: 'Recipient address',
})
recipientAddress: string;
@FieldMetadata({
@Field({
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
type: FieldMetadataType.SELECT,
type: FieldType.SELECT,
label: 'Status',
defaultValue: \`'\${PostCardStatus.DRAFT}'\`,
options: [
@@ -204,9 +203,9 @@ export class PostCard extends BaseObjectMetadata {
})
status: 'draft' | 'sent' | 'delivered' | 'returned';
@FieldMetadata({
@Field({
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
type: FieldMetadataType.DATE_TIME,
type: FieldType.DATE_TIME,
label: 'Delivered at',
isNullable: true,
defaultValue: null,
@@ -1,5 +1,5 @@
import { join } from 'path';
import { CURRENT_EXECUTION_DIRECTORY } from '../constants/current-execution-directory';
import { CURRENT_EXECUTION_DIRECTORY } from '@/cli/constants/current-execution-directory';
export const formatPath = (appPath?: string) => {
return appPath && !appPath?.startsWith('/')
@@ -1,6 +1,6 @@
import * as fs from 'fs-extra';
import dotenv from 'dotenv';
import { findPathFile } from './find-path-file';
import { findPathFile } from '@/cli/utils/find-path-file';
export const loadEnvVariables = async (appPath: string) => {
let envFile = '';
@@ -32,20 +32,20 @@ import {
isTemplateExpression,
isVariableStatement,
} from 'typescript';
import { GENERATED_FOLDER_NAME } from '../services/generate.service';
import { GENERATED_FOLDER_NAME } from '@/cli/services/generate.service';
import { type Sources } from 'twenty-shared/types';
import {
type AppManifest,
type Application,
type FieldMetadata,
type ObjectManifest,
type PackageJson,
type ApplicationManifest,
type ServerlessFunctionManifest,
type Sources,
} from '../types/config.types';
import { findPathFile } from '../utils/find-path-file';
import { getTsProgramAndDiagnostics } from '../utils/get-ts-program-and-diagnostics';
import { parseJsoncFile, parseTextFile } from '../utils/jsonc-parser';
import { formatAndWarnTsDiagnostics } from './format-and-warn-ts-diagnostics';
type ObjectManifest,
type FieldManifest,
} from 'twenty-shared/application';
import { findPathFile } from '@/cli/utils/find-path-file';
import { getTsProgramAndDiagnostics } from '@/cli/utils/get-ts-program-and-diagnostics';
import { parseJsoncFile, parseTextFile } from '@/cli/utils/jsonc-parser';
import { formatAndWarnTsDiagnostics } from '@/cli/utils/format-and-warn-ts-diagnostics';
type JSONValue =
| string
@@ -161,7 +161,7 @@ const collectObjects = (program: Program) => {
const fieldDec = getDecorators(member)?.find(
(d) =>
isDecoratorNamed(d, 'FieldMetadata') ||
isDecoratorNamed(d, 'FieldManifest') ||
isDecoratorNamed(d, 'Field'),
);
@@ -185,7 +185,7 @@ const collectObjects = (program: Program) => {
}
fields.push({
...(fieldCfg as FieldMetadata),
...(fieldCfg as FieldManifest),
...(name ? { name } : {}),
});
}
@@ -490,7 +490,7 @@ export const loadManifest = async (
): Promise<{
packageJson: PackageJson;
yarnLock: string;
manifest: AppManifest;
manifest: ApplicationManifest;
shouldGenerate: boolean;
}> => {
const packageJson = await parseJsoncFile(