Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e58f6ab6d9 | ||
|
|
c3b75d0ad2 | ||
|
|
e456642904 | ||
|
|
f855fa314d | ||
|
|
85b65d12d2 | ||
|
|
9dde87c20f | ||
|
|
cdaebdb16c | ||
|
|
3f3e42b743 | ||
|
|
22f3fd7a7a | ||
|
|
37e6353c1d | ||
|
|
d2eb3474c6 | ||
|
|
3587e195ac | ||
|
|
d54ea4fd11 | ||
|
|
2cd2f7c29c | ||
|
|
7535b90ec6 | ||
|
|
1fb51bb510 | ||
|
|
1bb257a811 | ||
|
|
aac099f2e8 | ||
|
|
a3c0c62506 | ||
|
|
390ae34ed8 | ||
|
|
fe59c333ac | ||
|
|
6c0da189cb | ||
|
|
5aea16f070 | ||
|
|
2f542d6763 | ||
|
|
4ac38449cb | ||
|
|
dab4cbcb7d | ||
|
|
a73f586619 | ||
|
|
d1126f507d | ||
|
|
3a1f10e572 | ||
|
|
d16299a624 | ||
|
|
61d058bb1f |
@@ -92,7 +92,7 @@ jobs:
|
||||
run: |
|
||||
cd packages/twenty-docker/
|
||||
echo "Seeding full dev workspace..."
|
||||
if ! docker compose exec -T server yarn command:prod -- workspace:seed:dev; then
|
||||
if ! docker compose exec -T server yarn command:prod -- workspace:seed:dev --with-apps; then
|
||||
echo "❌ Seeding full dev workspace failed. Dumping server logs..."
|
||||
docker compose logs server
|
||||
exit 1
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
generated
|
||||
.twenty
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
generated
|
||||
node_modules
|
||||
@@ -0,0 +1,12 @@
|
||||
import { defineApplication } from 'twenty-sdk';
|
||||
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from './src/roles/default-role';
|
||||
|
||||
export const APPLICATION_UNIVERSAL_IDENTIFIER =
|
||||
'6563e091-9f5b-4026-a3ea-7e3b3d09e218';
|
||||
|
||||
export default defineApplication({
|
||||
universalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
displayName: 'Hello world',
|
||||
description: '',
|
||||
defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
@@ -0,0 +1,404 @@
|
||||
{
|
||||
"application": {
|
||||
"universalIdentifier": "6563e091-9f5b-4026-a3ea-7e3b3d09e218",
|
||||
"displayName": "Hello world",
|
||||
"description": "",
|
||||
"defaultRoleUniversalIdentifier": "9238bc7b-d38f-4a1c-9d19-31ab7bc67a2f",
|
||||
"yarnLockChecksum": null,
|
||||
"packageJsonChecksum": null,
|
||||
"apiClientChecksum": "6db455b2541abbd2d4e9f85469bdc532",
|
||||
"preInstallLogicFunctionUniversalIdentifier": "1272ffdb-8e2f-492c-ab37-66c2b97e9c23",
|
||||
"postInstallLogicFunctionUniversalIdentifier": "7a3f4684-51db-494d-833b-a747a3b90507"
|
||||
},
|
||||
"objects": [
|
||||
{
|
||||
"universalIdentifier": "dfd43356-39b3-4b55-b4a7-279bec689928",
|
||||
"nameSingular": "exampleItem",
|
||||
"namePlural": "exampleItems",
|
||||
"labelSingular": "Example item",
|
||||
"labelPlural": "Example items",
|
||||
"description": "A sample custom object",
|
||||
"icon": "IconBox",
|
||||
"labelIdentifierFieldMetadataUniversalIdentifier": "d2d7f6cd-33f6-456f-bf00-17adeca926ba",
|
||||
"fields": [
|
||||
{
|
||||
"universalIdentifier": "d2d7f6cd-33f6-456f-bf00-17adeca926ba",
|
||||
"type": "TEXT",
|
||||
"name": "name",
|
||||
"label": "Name",
|
||||
"description": "Name of the example item",
|
||||
"icon": "IconAbc"
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"label": "Id",
|
||||
"description": "Id",
|
||||
"icon": "Icon123",
|
||||
"isNullable": false,
|
||||
"defaultValue": "uuid",
|
||||
"type": "UUID",
|
||||
"universalIdentifier": "5ba62991-6034-555c-b984-5e84897eaa9f"
|
||||
},
|
||||
{
|
||||
"name": "createdAt",
|
||||
"label": "Creation date",
|
||||
"description": "Creation date",
|
||||
"icon": "IconCalendar",
|
||||
"isNullable": false,
|
||||
"defaultValue": "now",
|
||||
"type": "DATE_TIME",
|
||||
"universalIdentifier": "8b616045-237c-574d-8689-73f06acff8d8"
|
||||
},
|
||||
{
|
||||
"name": "updatedAt",
|
||||
"label": "Last update",
|
||||
"description": "Last time the record was changed",
|
||||
"icon": "IconCalendarClock",
|
||||
"isNullable": false,
|
||||
"defaultValue": "now",
|
||||
"type": "DATE_TIME",
|
||||
"universalIdentifier": "ced5587d-d9de-51bf-9ad0-74af6198fca4"
|
||||
},
|
||||
{
|
||||
"name": "deletedAt",
|
||||
"label": "Deleted at",
|
||||
"description": "Deletion date",
|
||||
"icon": "IconCalendarClock",
|
||||
"isNullable": true,
|
||||
"defaultValue": null,
|
||||
"type": "DATE_TIME",
|
||||
"universalIdentifier": "674ee694-0b0f-557f-993c-2b1404db0811"
|
||||
},
|
||||
{
|
||||
"name": "createdBy",
|
||||
"label": "Created by",
|
||||
"description": "The creator of the record",
|
||||
"icon": "IconCreativeCommonsSa",
|
||||
"isNullable": false,
|
||||
"defaultValue": {
|
||||
"name": "''",
|
||||
"source": "'MANUAL'"
|
||||
},
|
||||
"type": "ACTOR",
|
||||
"universalIdentifier": "5ed1503f-2803-59d1-9b27-e4f1701b8c2a"
|
||||
},
|
||||
{
|
||||
"name": "updatedBy",
|
||||
"label": "Updated by",
|
||||
"description": "The workspace member who last updated the record",
|
||||
"icon": "IconUserCircle",
|
||||
"isNullable": false,
|
||||
"defaultValue": {
|
||||
"name": "''",
|
||||
"source": "'MANUAL'"
|
||||
},
|
||||
"type": "ACTOR",
|
||||
"universalIdentifier": "3d3a80b4-14a0-55a0-b534-1b23f043c1d5"
|
||||
},
|
||||
{
|
||||
"name": "position",
|
||||
"label": "Position",
|
||||
"description": "Position",
|
||||
"icon": "IconHierarchy2",
|
||||
"isNullable": false,
|
||||
"defaultValue": 0,
|
||||
"type": "POSITION",
|
||||
"universalIdentifier": "a714b336-d765-5dff-b34b-fcc6ab037e3b"
|
||||
},
|
||||
{
|
||||
"name": "searchVector",
|
||||
"label": "Search vector",
|
||||
"icon": "IconSearch",
|
||||
"description": "Search vector",
|
||||
"isNullable": true,
|
||||
"defaultValue": null,
|
||||
"type": "TS_VECTOR",
|
||||
"universalIdentifier": "068064e1-c9da-5772-9293-9e57183cee67"
|
||||
},
|
||||
{
|
||||
"name": "timelineActivities",
|
||||
"label": "Timeline Activities",
|
||||
"description": "Example items tied to the ExampleItem",
|
||||
"icon": "IconBuildingSkyscraper",
|
||||
"isNullable": true,
|
||||
"type": "RELATION",
|
||||
"universalSettings": {
|
||||
"relationType": "ONE_TO_MANY"
|
||||
},
|
||||
"universalIdentifier": "e5feca94-536c-5779-8c33-05e39705bcd7",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "3d140ea1-fc57-5c8c-96fd-7d1525ed6a69",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "20202020-6736-4337-b5c4-8b39fae325a5"
|
||||
},
|
||||
{
|
||||
"name": "favorites",
|
||||
"label": "Favorites",
|
||||
"description": "Example items tied to the ExampleItem",
|
||||
"icon": "IconBuildingSkyscraper",
|
||||
"isNullable": true,
|
||||
"type": "RELATION",
|
||||
"universalSettings": {
|
||||
"relationType": "ONE_TO_MANY"
|
||||
},
|
||||
"universalIdentifier": "3781cc69-9d3f-58d0-927c-c1c552f37a1d",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "3043fec0-091c-59ff-95ef-b9d95fbfa3e6",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "20202020-ab56-4e05-92a3-e2414a499860"
|
||||
},
|
||||
{
|
||||
"name": "attachments",
|
||||
"label": "Attachments",
|
||||
"description": "Example items tied to the ExampleItem",
|
||||
"icon": "IconBuildingSkyscraper",
|
||||
"isNullable": true,
|
||||
"type": "RELATION",
|
||||
"universalSettings": {
|
||||
"relationType": "ONE_TO_MANY"
|
||||
},
|
||||
"universalIdentifier": "3ad98da8-42e1-50e3-b73b-8c06fa720e2a",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "e1232464-8f83-5459-aa94-a40cda8d949a",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "20202020-bd3d-4c60-8dca-571c71d4447a"
|
||||
},
|
||||
{
|
||||
"name": "noteTargets",
|
||||
"label": "Note Targets",
|
||||
"description": "Example items tied to the ExampleItem",
|
||||
"icon": "IconBuildingSkyscraper",
|
||||
"isNullable": true,
|
||||
"type": "RELATION",
|
||||
"universalSettings": {
|
||||
"relationType": "ONE_TO_MANY"
|
||||
},
|
||||
"universalIdentifier": "67cae4ab-7d3d-5a66-8d06-ca24568ebe76",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "9eeb7b0d-37e4-5a93-97c9-3dbb77fdb318",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "20202020-fff0-4b44-be82-bda313884400"
|
||||
},
|
||||
{
|
||||
"name": "taskTargets",
|
||||
"label": "Task Targets",
|
||||
"description": "Example items tied to the ExampleItem",
|
||||
"icon": "IconBuildingSkyscraper",
|
||||
"isNullable": true,
|
||||
"type": "RELATION",
|
||||
"universalSettings": {
|
||||
"relationType": "ONE_TO_MANY"
|
||||
},
|
||||
"universalIdentifier": "a3b9f10c-f780-58cc-bbc9-402ed5f136f6",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "5d46d303-452d-5c9c-ab64-0a9768b87956",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "20202020-5a9a-44e8-95df-771cd06d0fb1"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"objectUniversalIdentifier": "dfd43356-39b3-4b55-b4a7-279bec689928",
|
||||
"universalIdentifier": "770d32c2-cf12-4ab2-b66d-73f92dc239b5",
|
||||
"type": "NUMBER",
|
||||
"name": "priority",
|
||||
"label": "Priority",
|
||||
"description": "Priority level for the example item (1-10)"
|
||||
},
|
||||
{
|
||||
"name": "targetExampleItem",
|
||||
"label": "ExampleItem",
|
||||
"description": "ExampleItem Example item",
|
||||
"icon": "IconTimelineEvent",
|
||||
"isNullable": true,
|
||||
"universalSettings": {
|
||||
"relationType": "MANY_TO_ONE",
|
||||
"onDelete": "SET_NULL",
|
||||
"joinColumnName": "targetExampleItemId"
|
||||
},
|
||||
"universalIdentifier": "3d140ea1-fc57-5c8c-96fd-7d1525ed6a69",
|
||||
"objectUniversalIdentifier": "20202020-6736-4337-b5c4-8b39fae325a5",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "e5feca94-536c-5779-8c33-05e39705bcd7",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "dfd43356-39b3-4b55-b4a7-279bec689928",
|
||||
"type": "MORPH_RELATION",
|
||||
"morphId": "20202020-9a2b-4c3d-a4e5-f6a7b8c9d0e1"
|
||||
},
|
||||
{
|
||||
"name": "targetExampleItem",
|
||||
"label": "ExampleItem",
|
||||
"description": "ExampleItem Example item",
|
||||
"icon": "IconHeart",
|
||||
"isNullable": true,
|
||||
"universalSettings": {
|
||||
"relationType": "MANY_TO_ONE",
|
||||
"onDelete": "SET_NULL",
|
||||
"joinColumnName": "targetExampleItemId"
|
||||
},
|
||||
"universalIdentifier": "3043fec0-091c-59ff-95ef-b9d95fbfa3e6",
|
||||
"objectUniversalIdentifier": "20202020-ab56-4e05-92a3-e2414a499860",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "3781cc69-9d3f-58d0-927c-c1c552f37a1d",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "dfd43356-39b3-4b55-b4a7-279bec689928",
|
||||
"type": "RELATION"
|
||||
},
|
||||
{
|
||||
"name": "targetExampleItem",
|
||||
"label": "ExampleItem",
|
||||
"description": "ExampleItem Example item",
|
||||
"icon": "IconFileImport",
|
||||
"isNullable": true,
|
||||
"universalSettings": {
|
||||
"relationType": "MANY_TO_ONE",
|
||||
"onDelete": "SET_NULL",
|
||||
"joinColumnName": "targetExampleItemId"
|
||||
},
|
||||
"universalIdentifier": "e1232464-8f83-5459-aa94-a40cda8d949a",
|
||||
"objectUniversalIdentifier": "20202020-bd3d-4c60-8dca-571c71d4447a",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "3ad98da8-42e1-50e3-b73b-8c06fa720e2a",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "dfd43356-39b3-4b55-b4a7-279bec689928",
|
||||
"type": "MORPH_RELATION",
|
||||
"morphId": "20202020-f634-435d-ab8d-e1168b375c69"
|
||||
},
|
||||
{
|
||||
"name": "targetExampleItem",
|
||||
"label": "ExampleItem",
|
||||
"description": "ExampleItem Example item",
|
||||
"icon": "IconCheckbox",
|
||||
"isNullable": true,
|
||||
"universalSettings": {
|
||||
"relationType": "MANY_TO_ONE",
|
||||
"onDelete": "SET_NULL",
|
||||
"joinColumnName": "targetExampleItemId"
|
||||
},
|
||||
"universalIdentifier": "9eeb7b0d-37e4-5a93-97c9-3dbb77fdb318",
|
||||
"objectUniversalIdentifier": "20202020-fff0-4b44-be82-bda313884400",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "67cae4ab-7d3d-5a66-8d06-ca24568ebe76",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "dfd43356-39b3-4b55-b4a7-279bec689928",
|
||||
"type": "MORPH_RELATION",
|
||||
"morphId": "20202020-f635-435d-ab8d-e1168b375c70"
|
||||
},
|
||||
{
|
||||
"name": "targetExampleItem",
|
||||
"label": "ExampleItem",
|
||||
"description": "ExampleItem Example item",
|
||||
"icon": "IconCheckbox",
|
||||
"isNullable": true,
|
||||
"universalSettings": {
|
||||
"relationType": "MANY_TO_ONE",
|
||||
"onDelete": "SET_NULL",
|
||||
"joinColumnName": "targetExampleItemId"
|
||||
},
|
||||
"universalIdentifier": "5d46d303-452d-5c9c-ab64-0a9768b87956",
|
||||
"objectUniversalIdentifier": "20202020-5a9a-44e8-95df-771cd06d0fb1",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "a3b9f10c-f780-58cc-bbc9-402ed5f136f6",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "dfd43356-39b3-4b55-b4a7-279bec689928",
|
||||
"type": "MORPH_RELATION",
|
||||
"morphId": "20202020-f636-435d-ab8d-e1168b375c71"
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
{
|
||||
"universalIdentifier": "9238bc7b-d38f-4a1c-9d19-31ab7bc67a2f",
|
||||
"label": "Hello world default function role",
|
||||
"description": "Hello world default function role",
|
||||
"canReadAllObjectRecords": true,
|
||||
"canUpdateAllObjectRecords": true,
|
||||
"canSoftDeleteAllObjectRecords": true,
|
||||
"canDestroyAllObjectRecords": false
|
||||
}
|
||||
],
|
||||
"skills": [
|
||||
{
|
||||
"universalIdentifier": "d0940029-9d3c-40be-903a-52d65393028f",
|
||||
"name": "example-skill",
|
||||
"label": "Example Skill",
|
||||
"description": "A sample skill for your application",
|
||||
"icon": "IconBrain",
|
||||
"content": "Add your skill instructions here. Skills provide context and capabilities to AI agents."
|
||||
}
|
||||
],
|
||||
"agents": [],
|
||||
"logicFunctions": [
|
||||
{
|
||||
"universalIdentifier": "2baa26eb-9aaf-4856-a4f4-30d6fd6480ee",
|
||||
"name": "hello-world-logic-function",
|
||||
"description": "A simple logic function",
|
||||
"timeoutSeconds": 5,
|
||||
"httpRouteTriggerSettings": {
|
||||
"path": "/hello-world-logic-function",
|
||||
"httpMethod": "GET",
|
||||
"isAuthRequired": false
|
||||
},
|
||||
"toolInputSchema": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"handlerName": "default.config.handler",
|
||||
"sourceHandlerPath": "src/logic-functions/hello-world.ts",
|
||||
"builtHandlerPath": "src/logic-functions/hello-world.mjs",
|
||||
"builtHandlerChecksum": "f1a0565e18366e6c531438631137d23b"
|
||||
},
|
||||
{
|
||||
"universalIdentifier": "7a3f4684-51db-494d-833b-a747a3b90507",
|
||||
"name": "post-install",
|
||||
"description": "Runs after installation to set up the application.",
|
||||
"timeoutSeconds": 300,
|
||||
"toolInputSchema": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"handlerName": "default.config.handler",
|
||||
"sourceHandlerPath": "src/logic-functions/post-install.ts",
|
||||
"builtHandlerPath": "src/logic-functions/post-install.mjs",
|
||||
"builtHandlerChecksum": "7b016a1819c4e2355a80a895464032a5"
|
||||
},
|
||||
{
|
||||
"universalIdentifier": "1272ffdb-8e2f-492c-ab37-66c2b97e9c23",
|
||||
"name": "pre-install",
|
||||
"description": "Runs before installation to prepare the application.",
|
||||
"timeoutSeconds": 300,
|
||||
"toolInputSchema": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"handlerName": "default.config.handler",
|
||||
"sourceHandlerPath": "src/logic-functions/pre-install.ts",
|
||||
"builtHandlerPath": "src/logic-functions/pre-install.mjs",
|
||||
"builtHandlerChecksum": "e2151e31924929390a5933ebf7419465"
|
||||
}
|
||||
],
|
||||
"frontComponents": [
|
||||
{
|
||||
"universalIdentifier": "d371f098-5b2c-42f0-898d-94459f1ee337",
|
||||
"name": "hello-world-front-component",
|
||||
"description": "A sample front component",
|
||||
"componentName": "HelloWorld",
|
||||
"sourceComponentPath": "src/front-components/hello-world.tsx",
|
||||
"builtComponentPath": "src/front-components/hello-world.mjs",
|
||||
"builtComponentChecksum": "826d5d3f633496d7c2a4f902589b83ed",
|
||||
"isHeadless": false
|
||||
}
|
||||
],
|
||||
"publicAssets": [],
|
||||
"views": [
|
||||
{
|
||||
"universalIdentifier": "e004df40-29f3-47ba-b39d-d3a5c444367a",
|
||||
"name": "All example items",
|
||||
"objectUniversalIdentifier": "dfd43356-39b3-4b55-b4a7-279bec689928",
|
||||
"icon": "IconList",
|
||||
"key": "INDEX",
|
||||
"position": 0,
|
||||
"fields": [
|
||||
{
|
||||
"universalIdentifier": "496c40c2-5766-419c-93bf-20fdad3f34bb",
|
||||
"fieldMetadataUniversalIdentifier": "d2d7f6cd-33f6-456f-bf00-17adeca926ba",
|
||||
"position": 0,
|
||||
"isVisible": true,
|
||||
"size": 200
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"navigationMenuItems": [
|
||||
{
|
||||
"universalIdentifier": "10f90627-e9c2-44b7-9742-bed77e3d1b17",
|
||||
"name": "example-navigation-menu-item",
|
||||
"icon": "IconList",
|
||||
"color": "blue",
|
||||
"position": 0,
|
||||
"viewUniversalIdentifier": "e004df40-29f3-47ba-b39d-d3a5c444367a"
|
||||
}
|
||||
],
|
||||
"pageLayouts": []
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "hello-world-app",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^24.5.0",
|
||||
"npm": "please-use-yarn",
|
||||
"yarn": ">=4.0.2"
|
||||
},
|
||||
"packageManager": "yarn@4.9.2",
|
||||
"scripts": {
|
||||
"app:dev": "twenty app:dev",
|
||||
"lint": "oxlint -c .oxlintrc.json .",
|
||||
"lint:fix": "oxlint --fix -c .oxlintrc.json ."
|
||||
},
|
||||
"dependencies": {
|
||||
"twenty-sdk": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.9.3",
|
||||
"@types/node": "^24.7.2",
|
||||
"@types/react": "^19.0.2",
|
||||
"react": "^19.0.2",
|
||||
"oxlint": "^0.16.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { defineField, FieldType } from 'twenty-sdk';
|
||||
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/objects/example-object';
|
||||
|
||||
export default defineField({
|
||||
objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
|
||||
universalIdentifier: '770d32c2-cf12-4ab2-b66d-73f92dc239b5',
|
||||
type: FieldType.NUMBER,
|
||||
name: 'priority',
|
||||
label: 'Priority',
|
||||
description: 'Priority level for the example item (1-10)',
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import { defineFrontComponent } from 'twenty-sdk';
|
||||
|
||||
export const HelloWorld = () => {
|
||||
return (
|
||||
<div style={{ padding: '20px', fontFamily: 'sans-serif' }}>
|
||||
<h1>Hello, World!</h1>
|
||||
<p>This is your first front component.</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: 'd371f098-5b2c-42f0-898d-94459f1ee337',
|
||||
name: 'hello-world-front-component',
|
||||
description: 'A sample front component',
|
||||
component: HelloWorld,
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
|
||||
const handler = async (): Promise<{ message: string }> => {
|
||||
return { message: 'Hello, World!' };
|
||||
};
|
||||
|
||||
export default defineLogicFunction({
|
||||
universalIdentifier: '2baa26eb-9aaf-4856-a4f4-30d6fd6480ee',
|
||||
name: 'hello-world-logic-function',
|
||||
description: 'A simple logic function',
|
||||
timeoutSeconds: 5,
|
||||
handler,
|
||||
httpRouteTriggerSettings: {
|
||||
path: '/hello-world-logic-function',
|
||||
httpMethod: 'GET',
|
||||
isAuthRequired: false,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { definePostInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';
|
||||
|
||||
const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
|
||||
console.log('Post install logic function executed successfully!', payload.previousVersion);
|
||||
};
|
||||
|
||||
export default definePostInstallLogicFunction({
|
||||
universalIdentifier: '7a3f4684-51db-494d-833b-a747a3b90507',
|
||||
name: 'post-install',
|
||||
description: 'Runs after installation to set up the application.',
|
||||
timeoutSeconds: 300,
|
||||
handler,
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { definePreInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';
|
||||
|
||||
const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
|
||||
console.log('Pre install logic function executed successfully!', payload.previousVersion);
|
||||
};
|
||||
|
||||
export default definePreInstallLogicFunction({
|
||||
universalIdentifier: '1272ffdb-8e2f-492c-ab37-66c2b97e9c23',
|
||||
name: 'pre-install',
|
||||
description: 'Runs before installation to prepare the application.',
|
||||
timeoutSeconds: 300,
|
||||
handler,
|
||||
});
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { defineNavigationMenuItem } from 'twenty-sdk';
|
||||
import { EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER } from 'src/views/example-view';
|
||||
|
||||
export default defineNavigationMenuItem({
|
||||
universalIdentifier: '10f90627-e9c2-44b7-9742-bed77e3d1b17',
|
||||
name: 'example-navigation-menu-item',
|
||||
icon: 'IconList',
|
||||
color: 'blue',
|
||||
position: 0,
|
||||
viewUniversalIdentifier: EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
import { defineObject, FieldType } from 'twenty-sdk';
|
||||
|
||||
export const EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER =
|
||||
'dfd43356-39b3-4b55-b4a7-279bec689928';
|
||||
|
||||
export const NAME_FIELD_UNIVERSAL_IDENTIFIER =
|
||||
'd2d7f6cd-33f6-456f-bf00-17adeca926ba';
|
||||
|
||||
export default defineObject({
|
||||
universalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
|
||||
nameSingular: 'exampleItem',
|
||||
namePlural: 'exampleItems',
|
||||
labelSingular: 'Example item',
|
||||
labelPlural: 'Example items',
|
||||
description: 'A sample custom object',
|
||||
icon: 'IconBox',
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: NAME_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: NAME_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
type: FieldType.TEXT,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
description: 'Name of the example item',
|
||||
icon: 'IconAbc',
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
import { defineRole } from 'twenty-sdk';
|
||||
|
||||
export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =
|
||||
'9238bc7b-d38f-4a1c-9d19-31ab7bc67a2f';
|
||||
|
||||
export default defineRole({
|
||||
universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
||||
label: 'Hello world default function role',
|
||||
description: 'Hello world default function role',
|
||||
canReadAllObjectRecords: true,
|
||||
canUpdateAllObjectRecords: true,
|
||||
canSoftDeleteAllObjectRecords: true,
|
||||
canDestroyAllObjectRecords: false,
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { defineSkill } from 'twenty-sdk';
|
||||
|
||||
export const EXAMPLE_SKILL_UNIVERSAL_IDENTIFIER =
|
||||
'd0940029-9d3c-40be-903a-52d65393028f';
|
||||
|
||||
export default defineSkill({
|
||||
universalIdentifier: EXAMPLE_SKILL_UNIVERSAL_IDENTIFIER,
|
||||
name: 'example-skill',
|
||||
label: 'Example Skill',
|
||||
description: 'A sample skill for your application',
|
||||
icon: 'IconBrain',
|
||||
content: 'Add your skill instructions here. Skills provide context and capabilities to AI agents.',
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import { defineView } from 'twenty-sdk';
|
||||
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER, NAME_FIELD_UNIVERSAL_IDENTIFIER } from 'src/objects/example-object';
|
||||
|
||||
export const EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER = 'e004df40-29f3-47ba-b39d-d3a5c444367a';
|
||||
|
||||
export default defineView({
|
||||
universalIdentifier: EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER,
|
||||
name: 'All example items',
|
||||
objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
|
||||
icon: 'IconList',
|
||||
key: 'INDEX',
|
||||
position: 0,
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: '496c40c2-5766-419c-93bf-20fdad3f34bb',
|
||||
fieldMetadataUniversalIdentifier: NAME_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 200,
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"jsx": "react-jsx",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"src/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["**/*"],
|
||||
"exclude": ["node_modules", "dist", ".twenty"]
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
{
|
||||
"application": {
|
||||
"universalIdentifier": "e1e2e3e4-e5e6-4000-8000-000000000001",
|
||||
"displayName": "Root App",
|
||||
"description": "An app with all entities at root level",
|
||||
"icon": "IconFolder",
|
||||
"defaultRoleUniversalIdentifier": "e1e2e3e4-e5e6-4000-8000-000000000040",
|
||||
"yarnLockChecksum": "d41d8cd98f00b204e9800998ecf8427e",
|
||||
"packageJsonChecksum": "ac87abb5113d9d09be71481efc123800",
|
||||
"apiClientChecksum": null
|
||||
},
|
||||
"objects": [
|
||||
{
|
||||
"universalIdentifier": "e1e2e3e4-e5e6-4000-8000-000000000030",
|
||||
"nameSingular": "myNote",
|
||||
"namePlural": "myNotes",
|
||||
"labelSingular": "My note",
|
||||
"labelPlural": "My notes",
|
||||
"description": "A simple root-level object",
|
||||
"icon": "IconNote",
|
||||
"labelIdentifierFieldMetadataUniversalIdentifier": "e1e2e3e4-e5e6-4000-8000-000000000031",
|
||||
"fields": [
|
||||
{
|
||||
"universalIdentifier": "e1e2e3e4-e5e6-4000-8000-000000000031",
|
||||
"type": "TEXT",
|
||||
"label": "Title",
|
||||
"name": "title"
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"label": "Id",
|
||||
"description": "Id",
|
||||
"icon": "Icon123",
|
||||
"isNullable": false,
|
||||
"defaultValue": "uuid",
|
||||
"type": "UUID",
|
||||
"universalIdentifier": "5bdb4f57-a349-56d0-b421-845fd55b3026"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"label": "Name",
|
||||
"description": "Name",
|
||||
"icon": "IconAbc",
|
||||
"isNullable": true,
|
||||
"defaultValue": null,
|
||||
"type": "TEXT",
|
||||
"universalIdentifier": "86cc7b06-26de-5b7b-bf4a-31fa246dfd90"
|
||||
},
|
||||
{
|
||||
"name": "createdAt",
|
||||
"label": "Creation date",
|
||||
"description": "Creation date",
|
||||
"icon": "IconCalendar",
|
||||
"isNullable": false,
|
||||
"defaultValue": "now",
|
||||
"type": "DATE_TIME",
|
||||
"universalIdentifier": "abe3bc69-1a51-5814-a722-74f5a025e92a"
|
||||
},
|
||||
{
|
||||
"name": "updatedAt",
|
||||
"label": "Last update",
|
||||
"description": "Last time the record was changed",
|
||||
"icon": "IconCalendarClock",
|
||||
"isNullable": false,
|
||||
"defaultValue": "now",
|
||||
"type": "DATE_TIME",
|
||||
"universalIdentifier": "c296d408-9720-5a86-8586-3490fbf04b86"
|
||||
},
|
||||
{
|
||||
"name": "deletedAt",
|
||||
"label": "Deleted at",
|
||||
"description": "Deletion date",
|
||||
"icon": "IconCalendarClock",
|
||||
"isNullable": true,
|
||||
"defaultValue": null,
|
||||
"type": "DATE_TIME",
|
||||
"universalIdentifier": "488333a2-f4ff-5790-88ef-21f07f8f1985"
|
||||
},
|
||||
{
|
||||
"name": "createdBy",
|
||||
"label": "Created by",
|
||||
"description": "The creator of the record",
|
||||
"icon": "IconCreativeCommonsSa",
|
||||
"isNullable": false,
|
||||
"defaultValue": {
|
||||
"name": "''",
|
||||
"source": "'MANUAL'"
|
||||
},
|
||||
"type": "ACTOR",
|
||||
"universalIdentifier": "fba6e01b-6579-554e-ad64-a676003c8e68"
|
||||
},
|
||||
{
|
||||
"name": "updatedBy",
|
||||
"label": "Updated by",
|
||||
"description": "The workspace member who last updated the record",
|
||||
"icon": "IconUserCircle",
|
||||
"isNullable": false,
|
||||
"defaultValue": {
|
||||
"name": "''",
|
||||
"source": "'MANUAL'"
|
||||
},
|
||||
"type": "ACTOR",
|
||||
"universalIdentifier": "fa60f87d-15c8-5ff7-9da0-191fa14a922f"
|
||||
},
|
||||
{
|
||||
"name": "position",
|
||||
"label": "Position",
|
||||
"description": "Position",
|
||||
"icon": "IconHierarchy2",
|
||||
"isNullable": false,
|
||||
"defaultValue": 0,
|
||||
"type": "POSITION",
|
||||
"universalIdentifier": "4a5d7791-ddc9-53ee-bc46-699da7373290"
|
||||
},
|
||||
{
|
||||
"name": "searchVector",
|
||||
"label": "Search vector",
|
||||
"icon": "IconSearch",
|
||||
"description": "Search vector",
|
||||
"isNullable": true,
|
||||
"defaultValue": null,
|
||||
"type": "TS_VECTOR",
|
||||
"universalIdentifier": "0f00bc3b-f313-5206-8496-842af20a538a"
|
||||
},
|
||||
{
|
||||
"name": "timelineActivities",
|
||||
"label": "Timeline Activities",
|
||||
"description": "My notes tied to the MyNote",
|
||||
"icon": "IconBuildingSkyscraper",
|
||||
"isNullable": true,
|
||||
"type": "RELATION",
|
||||
"universalSettings": {
|
||||
"relationType": "ONE_TO_MANY"
|
||||
},
|
||||
"universalIdentifier": "12c84c1c-6e3f-54c5-a9a8-e93c7b46ac35",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "7e28b1ea-9a94-5d36-bc03-3f12f476867a",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "20202020-6736-4337-b5c4-8b39fae325a5"
|
||||
},
|
||||
{
|
||||
"name": "favorites",
|
||||
"label": "Favorites",
|
||||
"description": "My notes tied to the MyNote",
|
||||
"icon": "IconBuildingSkyscraper",
|
||||
"isNullable": true,
|
||||
"type": "RELATION",
|
||||
"universalSettings": {
|
||||
"relationType": "ONE_TO_MANY"
|
||||
},
|
||||
"universalIdentifier": "ed80c73b-25ff-5a3c-99c7-e27fa2f2611e",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "698f12fe-3feb-55e3-b6cc-73375f1a8ae6",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "20202020-ab56-4e05-92a3-e2414a499860"
|
||||
},
|
||||
{
|
||||
"name": "attachments",
|
||||
"label": "Attachments",
|
||||
"description": "My notes tied to the MyNote",
|
||||
"icon": "IconBuildingSkyscraper",
|
||||
"isNullable": true,
|
||||
"type": "RELATION",
|
||||
"universalSettings": {
|
||||
"relationType": "ONE_TO_MANY"
|
||||
},
|
||||
"universalIdentifier": "56a872c8-7a1e-563d-8772-3978c2c5c220",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "721b12d8-7a3d-5e13-b8d5-62b3ad8cb8b5",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "20202020-bd3d-4c60-8dca-571c71d4447a"
|
||||
},
|
||||
{
|
||||
"name": "noteTargets",
|
||||
"label": "Note Targets",
|
||||
"description": "My notes tied to the MyNote",
|
||||
"icon": "IconBuildingSkyscraper",
|
||||
"isNullable": true,
|
||||
"type": "RELATION",
|
||||
"universalSettings": {
|
||||
"relationType": "ONE_TO_MANY"
|
||||
},
|
||||
"universalIdentifier": "011595e8-7524-5ec7-9fbc-4bd3c42d7d24",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "94b6f269-fc9a-5bcd-af5b-2bdac69f276b",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "20202020-fff0-4b44-be82-bda313884400"
|
||||
},
|
||||
{
|
||||
"name": "taskTargets",
|
||||
"label": "Task Targets",
|
||||
"description": "My notes tied to the MyNote",
|
||||
"icon": "IconBuildingSkyscraper",
|
||||
"isNullable": true,
|
||||
"type": "RELATION",
|
||||
"universalSettings": {
|
||||
"relationType": "ONE_TO_MANY"
|
||||
},
|
||||
"universalIdentifier": "dd734fa3-26c3-53dd-91aa-577c313e1fa4",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "50da3cfa-dea5-5c9b-b1b7-cb615e1df4c3",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "20202020-5a9a-44e8-95df-771cd06d0fb1"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"name": "targetMyNote",
|
||||
"label": "MyNote",
|
||||
"description": "MyNote My note",
|
||||
"icon": "IconTimelineEvent",
|
||||
"isNullable": true,
|
||||
"universalSettings": {
|
||||
"relationType": "MANY_TO_ONE",
|
||||
"onDelete": "SET_NULL",
|
||||
"joinColumnName": "targetMyNoteId"
|
||||
},
|
||||
"universalIdentifier": "7e28b1ea-9a94-5d36-bc03-3f12f476867a",
|
||||
"objectUniversalIdentifier": "20202020-6736-4337-b5c4-8b39fae325a5",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "12c84c1c-6e3f-54c5-a9a8-e93c7b46ac35",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "e1e2e3e4-e5e6-4000-8000-000000000030",
|
||||
"type": "MORPH_RELATION",
|
||||
"morphId": "20202020-9a2b-4c3d-a4e5-f6a7b8c9d0e1"
|
||||
},
|
||||
{
|
||||
"name": "targetMyNote",
|
||||
"label": "MyNote",
|
||||
"description": "MyNote My note",
|
||||
"icon": "IconHeart",
|
||||
"isNullable": true,
|
||||
"universalSettings": {
|
||||
"relationType": "MANY_TO_ONE",
|
||||
"onDelete": "SET_NULL",
|
||||
"joinColumnName": "targetMyNoteId"
|
||||
},
|
||||
"universalIdentifier": "698f12fe-3feb-55e3-b6cc-73375f1a8ae6",
|
||||
"objectUniversalIdentifier": "20202020-ab56-4e05-92a3-e2414a499860",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "ed80c73b-25ff-5a3c-99c7-e27fa2f2611e",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "e1e2e3e4-e5e6-4000-8000-000000000030",
|
||||
"type": "RELATION"
|
||||
},
|
||||
{
|
||||
"name": "targetMyNote",
|
||||
"label": "MyNote",
|
||||
"description": "MyNote My note",
|
||||
"icon": "IconFileImport",
|
||||
"isNullable": true,
|
||||
"universalSettings": {
|
||||
"relationType": "MANY_TO_ONE",
|
||||
"onDelete": "SET_NULL",
|
||||
"joinColumnName": "targetMyNoteId"
|
||||
},
|
||||
"universalIdentifier": "721b12d8-7a3d-5e13-b8d5-62b3ad8cb8b5",
|
||||
"objectUniversalIdentifier": "20202020-bd3d-4c60-8dca-571c71d4447a",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "56a872c8-7a1e-563d-8772-3978c2c5c220",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "e1e2e3e4-e5e6-4000-8000-000000000030",
|
||||
"type": "MORPH_RELATION",
|
||||
"morphId": "20202020-f634-435d-ab8d-e1168b375c69"
|
||||
},
|
||||
{
|
||||
"name": "targetMyNote",
|
||||
"label": "MyNote",
|
||||
"description": "MyNote My note",
|
||||
"icon": "IconCheckbox",
|
||||
"isNullable": true,
|
||||
"universalSettings": {
|
||||
"relationType": "MANY_TO_ONE",
|
||||
"onDelete": "SET_NULL",
|
||||
"joinColumnName": "targetMyNoteId"
|
||||
},
|
||||
"universalIdentifier": "94b6f269-fc9a-5bcd-af5b-2bdac69f276b",
|
||||
"objectUniversalIdentifier": "20202020-fff0-4b44-be82-bda313884400",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "011595e8-7524-5ec7-9fbc-4bd3c42d7d24",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "e1e2e3e4-e5e6-4000-8000-000000000030",
|
||||
"type": "MORPH_RELATION",
|
||||
"morphId": "20202020-f635-435d-ab8d-e1168b375c70"
|
||||
},
|
||||
{
|
||||
"name": "targetMyNote",
|
||||
"label": "MyNote",
|
||||
"description": "MyNote My note",
|
||||
"icon": "IconCheckbox",
|
||||
"isNullable": true,
|
||||
"universalSettings": {
|
||||
"relationType": "MANY_TO_ONE",
|
||||
"onDelete": "SET_NULL",
|
||||
"joinColumnName": "targetMyNoteId"
|
||||
},
|
||||
"universalIdentifier": "50da3cfa-dea5-5c9b-b1b7-cb615e1df4c3",
|
||||
"objectUniversalIdentifier": "20202020-5a9a-44e8-95df-771cd06d0fb1",
|
||||
"relationTargetFieldMetadataUniversalIdentifier": "dd734fa3-26c3-53dd-91aa-577c313e1fa4",
|
||||
"relationTargetObjectMetadataUniversalIdentifier": "e1e2e3e4-e5e6-4000-8000-000000000030",
|
||||
"type": "MORPH_RELATION",
|
||||
"morphId": "20202020-f636-435d-ab8d-e1168b375c71"
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
{
|
||||
"universalIdentifier": "e1e2e3e4-e5e6-4000-8000-000000000040",
|
||||
"label": "My role",
|
||||
"description": "A simple root-level role",
|
||||
"canReadAllObjectRecords": true,
|
||||
"canUpdateAllObjectRecords": false,
|
||||
"canSoftDeleteAllObjectRecords": false,
|
||||
"canDestroyAllObjectRecords": false,
|
||||
"canUpdateAllSettings": false,
|
||||
"canBeAssignedToAgents": false,
|
||||
"canBeAssignedToUsers": true,
|
||||
"canBeAssignedToApiKeys": false
|
||||
}
|
||||
],
|
||||
"skills": [],
|
||||
"agents": [],
|
||||
"logicFunctions": [
|
||||
{
|
||||
"universalIdentifier": "e1e2e3e4-e5e6-4000-8000-000000000010",
|
||||
"name": "my-function",
|
||||
"timeoutSeconds": 5,
|
||||
"httpRouteTriggerSettings": {
|
||||
"path": "/my-function",
|
||||
"httpMethod": "GET",
|
||||
"isAuthRequired": false
|
||||
},
|
||||
"toolInputSchema": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"handlerName": "default.config.handler",
|
||||
"sourceHandlerPath": "my.function.ts",
|
||||
"builtHandlerPath": "my.function.mjs",
|
||||
"builtHandlerChecksum": "71f0af6b9dfeef884cc81ae75ca6ecb0"
|
||||
}
|
||||
],
|
||||
"frontComponents": [
|
||||
{
|
||||
"universalIdentifier": "e1e2e3e4-e5e6-4000-8000-000000000020",
|
||||
"name": "my-component",
|
||||
"description": "A root-level front component",
|
||||
"componentName": "MyComponent",
|
||||
"sourceComponentPath": "my.front-component.tsx",
|
||||
"builtComponentPath": "my.front-component.mjs",
|
||||
"builtComponentChecksum": "37b905c5467584f03fa5ab2c8a05bc88",
|
||||
"isHeadless": false
|
||||
}
|
||||
],
|
||||
"publicAssets": [],
|
||||
"views": [],
|
||||
"navigationMenuItems": [],
|
||||
"pageLayouts": []
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,6 +29,7 @@ COPY ./packages/twenty-shared /app/packages/twenty-shared
|
||||
COPY ./packages/twenty-ui /app/packages/twenty-ui
|
||||
COPY ./packages/twenty-sdk /app/packages/twenty-sdk
|
||||
COPY ./packages/twenty-server /app/packages/twenty-server
|
||||
COPY ./packages/twenty-apps /app/packages/twenty-apps
|
||||
|
||||
RUN npx nx run twenty-server:build
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { resolve } from 'path';
|
||||
|
||||
const FIXTURES_ROOT = resolve(__dirname, '../../../../../twenty-apps/fixtures');
|
||||
const TWENTY_APPS_ROOT = resolve(__dirname, '../../../../../twenty-apps');
|
||||
|
||||
export const POSTCARD_APP_PATH = resolve(FIXTURES_ROOT, 'postcard-app');
|
||||
export const MINIMAL_APP_PATH = resolve(FIXTURES_ROOT, 'minimal-app');
|
||||
@@ -9,3 +10,4 @@ export const FUNCTION_EXECUTE_APP_PATH = resolve(
|
||||
FIXTURES_ROOT,
|
||||
'function-execute-app',
|
||||
);
|
||||
export const HELLO_WORLD_APP_PATH = resolve(TWENTY_APPS_ROOT, 'hello-world');
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { runAppDevInProcess } from '@/cli/__tests__/integration/utils/run-app-dev-in-process.util';
|
||||
import { HELLO_WORLD_APP_PATH } from '@/cli/__tests__/apps/fixture-paths';
|
||||
|
||||
import { defineManifestTests } from './tests/manifest.tests';
|
||||
|
||||
describe('hello-world app:dev', () => {
|
||||
beforeAll(async () => {
|
||||
const result = await runAppDevInProcess({ appPath: HELLO_WORLD_APP_PATH });
|
||||
|
||||
if (!result.success) {
|
||||
const diagnostics = JSON.stringify(
|
||||
{ events: result.events, stepStatuses: result.stepStatuses },
|
||||
null,
|
||||
2,
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`app:dev did not produce manifest.json within timeout.\n${diagnostics}`,
|
||||
);
|
||||
}
|
||||
}, 60000);
|
||||
|
||||
defineManifestTests(HELLO_WORLD_APP_PATH);
|
||||
});
|
||||
+429
@@ -0,0 +1,429 @@
|
||||
import { FieldType } from '@/sdk';
|
||||
import type { Manifest } from 'twenty-shared/application';
|
||||
import {
|
||||
FieldMetadataType,
|
||||
RelationOnDeleteAction,
|
||||
RelationType,
|
||||
ViewKey,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
export const EXPECTED_MANIFEST: Manifest = {
|
||||
application: {
|
||||
universalIdentifier: '6563e091-9f5b-4026-a3ea-7e3b3d09e218',
|
||||
displayName: 'Hello world',
|
||||
description: '',
|
||||
defaultRoleUniversalIdentifier: '9238bc7b-d38f-4a1c-9d19-31ab7bc67a2f',
|
||||
yarnLockChecksum: '[checksum]',
|
||||
packageJsonChecksum: '[checksum]',
|
||||
apiClientChecksum: null,
|
||||
preInstallLogicFunctionUniversalIdentifier:
|
||||
'1272ffdb-8e2f-492c-ab37-66c2b97e9c23',
|
||||
postInstallLogicFunctionUniversalIdentifier:
|
||||
'7a3f4684-51db-494d-833b-a747a3b90507',
|
||||
},
|
||||
objects: [
|
||||
{
|
||||
universalIdentifier: 'dfd43356-39b3-4b55-b4a7-279bec689928',
|
||||
nameSingular: 'exampleItem',
|
||||
namePlural: 'exampleItems',
|
||||
labelSingular: 'Example item',
|
||||
labelPlural: 'Example items',
|
||||
description: 'A sample custom object',
|
||||
icon: 'IconBox',
|
||||
labelIdentifierFieldMetadataUniversalIdentifier:
|
||||
'd2d7f6cd-33f6-456f-bf00-17adeca926ba',
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: 'd2d7f6cd-33f6-456f-bf00-17adeca926ba',
|
||||
type: FieldType.TEXT,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
description: 'Name of the example item',
|
||||
icon: 'IconAbc',
|
||||
},
|
||||
{
|
||||
name: 'id',
|
||||
label: 'Id',
|
||||
description: 'Id',
|
||||
icon: 'Icon123',
|
||||
isNullable: false,
|
||||
defaultValue: 'uuid',
|
||||
type: FieldMetadataType.UUID,
|
||||
universalIdentifier: '5ba62991-6034-555c-b984-5e84897eaa9f',
|
||||
},
|
||||
{
|
||||
name: 'createdAt',
|
||||
label: 'Creation date',
|
||||
description: 'Creation date',
|
||||
icon: 'IconCalendar',
|
||||
isNullable: false,
|
||||
defaultValue: 'now',
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
universalIdentifier: '8b616045-237c-574d-8689-73f06acff8d8',
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Last update',
|
||||
description: 'Last time the record was changed',
|
||||
icon: 'IconCalendarClock',
|
||||
isNullable: false,
|
||||
defaultValue: 'now',
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
universalIdentifier: 'ced5587d-d9de-51bf-9ad0-74af6198fca4',
|
||||
},
|
||||
{
|
||||
name: 'deletedAt',
|
||||
label: 'Deleted at',
|
||||
description: 'Deletion date',
|
||||
icon: 'IconCalendarClock',
|
||||
isNullable: true,
|
||||
defaultValue: null,
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
universalIdentifier: '674ee694-0b0f-557f-993c-2b1404db0811',
|
||||
},
|
||||
{
|
||||
name: 'createdBy',
|
||||
label: 'Created by',
|
||||
description: 'The creator of the record',
|
||||
icon: 'IconCreativeCommonsSa',
|
||||
isNullable: false,
|
||||
defaultValue: {
|
||||
name: "''",
|
||||
source: "'MANUAL'",
|
||||
},
|
||||
type: FieldMetadataType.ACTOR,
|
||||
universalIdentifier: '5ed1503f-2803-59d1-9b27-e4f1701b8c2a',
|
||||
},
|
||||
{
|
||||
name: 'updatedBy',
|
||||
label: 'Updated by',
|
||||
description: 'The workspace member who last updated the record',
|
||||
icon: 'IconUserCircle',
|
||||
isNullable: false,
|
||||
defaultValue: {
|
||||
name: "''",
|
||||
source: "'MANUAL'",
|
||||
},
|
||||
type: FieldMetadataType.ACTOR,
|
||||
universalIdentifier: '3d3a80b4-14a0-55a0-b534-1b23f043c1d5',
|
||||
},
|
||||
{
|
||||
name: 'position',
|
||||
label: 'Position',
|
||||
description: 'Position',
|
||||
icon: 'IconHierarchy2',
|
||||
isNullable: false,
|
||||
defaultValue: 0,
|
||||
type: FieldMetadataType.POSITION,
|
||||
universalIdentifier: 'a714b336-d765-5dff-b34b-fcc6ab037e3b',
|
||||
},
|
||||
{
|
||||
name: 'searchVector',
|
||||
label: 'Search vector',
|
||||
icon: 'IconSearch',
|
||||
description: 'Search vector',
|
||||
isNullable: true,
|
||||
defaultValue: null,
|
||||
type: FieldMetadataType.TS_VECTOR,
|
||||
universalIdentifier: '068064e1-c9da-5772-9293-9e57183cee67',
|
||||
},
|
||||
{
|
||||
name: 'timelineActivities',
|
||||
label: 'Timeline Activities',
|
||||
description: 'Example items tied to the ExampleItem',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
isNullable: true,
|
||||
type: FieldType.RELATION,
|
||||
universalSettings: {
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
},
|
||||
universalIdentifier: 'e5feca94-536c-5779-8c33-05e39705bcd7',
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
'3d140ea1-fc57-5c8c-96fd-7d1525ed6a69',
|
||||
relationTargetObjectMetadataUniversalIdentifier:
|
||||
'20202020-6736-4337-b5c4-8b39fae325a5',
|
||||
},
|
||||
{
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
description: 'Example items tied to the ExampleItem',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
isNullable: true,
|
||||
type: FieldType.RELATION,
|
||||
universalSettings: {
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
},
|
||||
universalIdentifier: '3781cc69-9d3f-58d0-927c-c1c552f37a1d',
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
'3043fec0-091c-59ff-95ef-b9d95fbfa3e6',
|
||||
relationTargetObjectMetadataUniversalIdentifier:
|
||||
'20202020-ab56-4e05-92a3-e2414a499860',
|
||||
},
|
||||
{
|
||||
name: 'attachments',
|
||||
label: 'Attachments',
|
||||
description: 'Example items tied to the ExampleItem',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
isNullable: true,
|
||||
type: FieldType.RELATION,
|
||||
universalSettings: {
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
},
|
||||
universalIdentifier: '3ad98da8-42e1-50e3-b73b-8c06fa720e2a',
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
'e1232464-8f83-5459-aa94-a40cda8d949a',
|
||||
relationTargetObjectMetadataUniversalIdentifier:
|
||||
'20202020-bd3d-4c60-8dca-571c71d4447a',
|
||||
},
|
||||
{
|
||||
name: 'noteTargets',
|
||||
label: 'Note Targets',
|
||||
description: 'Example items tied to the ExampleItem',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
isNullable: true,
|
||||
type: FieldType.RELATION,
|
||||
universalSettings: {
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
},
|
||||
universalIdentifier: '67cae4ab-7d3d-5a66-8d06-ca24568ebe76',
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
'9eeb7b0d-37e4-5a93-97c9-3dbb77fdb318',
|
||||
relationTargetObjectMetadataUniversalIdentifier:
|
||||
'20202020-fff0-4b44-be82-bda313884400',
|
||||
},
|
||||
{
|
||||
name: 'taskTargets',
|
||||
label: 'Task Targets',
|
||||
description: 'Example items tied to the ExampleItem',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
isNullable: true,
|
||||
type: FieldType.RELATION,
|
||||
universalSettings: {
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
},
|
||||
universalIdentifier: 'a3b9f10c-f780-58cc-bbc9-402ed5f136f6',
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
'5d46d303-452d-5c9c-ab64-0a9768b87956',
|
||||
relationTargetObjectMetadataUniversalIdentifier:
|
||||
'20202020-5a9a-44e8-95df-771cd06d0fb1',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
objectUniversalIdentifier: 'dfd43356-39b3-4b55-b4a7-279bec689928',
|
||||
universalIdentifier: '770d32c2-cf12-4ab2-b66d-73f92dc239b5',
|
||||
type: FieldType.NUMBER,
|
||||
name: 'priority',
|
||||
label: 'Priority',
|
||||
description: 'Priority level for the example item (1-10)',
|
||||
},
|
||||
{
|
||||
name: 'targetExampleItem',
|
||||
label: 'ExampleItem',
|
||||
description: 'ExampleItem Example item',
|
||||
icon: 'IconTimelineEvent',
|
||||
isNullable: true,
|
||||
universalSettings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
joinColumnName: 'targetExampleItemId',
|
||||
},
|
||||
universalIdentifier: '3d140ea1-fc57-5c8c-96fd-7d1525ed6a69',
|
||||
objectUniversalIdentifier: '20202020-6736-4337-b5c4-8b39fae325a5',
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
'e5feca94-536c-5779-8c33-05e39705bcd7',
|
||||
relationTargetObjectMetadataUniversalIdentifier:
|
||||
'dfd43356-39b3-4b55-b4a7-279bec689928',
|
||||
type: FieldType.MORPH_RELATION,
|
||||
morphId: '20202020-9a2b-4c3d-a4e5-f6a7b8c9d0e1',
|
||||
},
|
||||
{
|
||||
name: 'targetExampleItem',
|
||||
label: 'ExampleItem',
|
||||
description: 'ExampleItem Example item',
|
||||
icon: 'IconHeart',
|
||||
isNullable: true,
|
||||
universalSettings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
joinColumnName: 'targetExampleItemId',
|
||||
},
|
||||
universalIdentifier: '3043fec0-091c-59ff-95ef-b9d95fbfa3e6',
|
||||
objectUniversalIdentifier: '20202020-ab56-4e05-92a3-e2414a499860',
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
'3781cc69-9d3f-58d0-927c-c1c552f37a1d',
|
||||
relationTargetObjectMetadataUniversalIdentifier:
|
||||
'dfd43356-39b3-4b55-b4a7-279bec689928',
|
||||
type: FieldType.RELATION,
|
||||
},
|
||||
{
|
||||
name: 'targetExampleItem',
|
||||
label: 'ExampleItem',
|
||||
description: 'ExampleItem Example item',
|
||||
icon: 'IconFileImport',
|
||||
isNullable: true,
|
||||
universalSettings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
joinColumnName: 'targetExampleItemId',
|
||||
},
|
||||
universalIdentifier: 'e1232464-8f83-5459-aa94-a40cda8d949a',
|
||||
objectUniversalIdentifier: '20202020-bd3d-4c60-8dca-571c71d4447a',
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
'3ad98da8-42e1-50e3-b73b-8c06fa720e2a',
|
||||
relationTargetObjectMetadataUniversalIdentifier:
|
||||
'dfd43356-39b3-4b55-b4a7-279bec689928',
|
||||
type: FieldType.MORPH_RELATION,
|
||||
morphId: '20202020-f634-435d-ab8d-e1168b375c69',
|
||||
},
|
||||
{
|
||||
name: 'targetExampleItem',
|
||||
label: 'ExampleItem',
|
||||
description: 'ExampleItem Example item',
|
||||
icon: 'IconCheckbox',
|
||||
isNullable: true,
|
||||
universalSettings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
joinColumnName: 'targetExampleItemId',
|
||||
},
|
||||
universalIdentifier: '9eeb7b0d-37e4-5a93-97c9-3dbb77fdb318',
|
||||
objectUniversalIdentifier: '20202020-fff0-4b44-be82-bda313884400',
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
'67cae4ab-7d3d-5a66-8d06-ca24568ebe76',
|
||||
relationTargetObjectMetadataUniversalIdentifier:
|
||||
'dfd43356-39b3-4b55-b4a7-279bec689928',
|
||||
type: FieldType.MORPH_RELATION,
|
||||
morphId: '20202020-f635-435d-ab8d-e1168b375c70',
|
||||
},
|
||||
{
|
||||
name: 'targetExampleItem',
|
||||
label: 'ExampleItem',
|
||||
description: 'ExampleItem Example item',
|
||||
icon: 'IconCheckbox',
|
||||
isNullable: true,
|
||||
universalSettings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
joinColumnName: 'targetExampleItemId',
|
||||
},
|
||||
universalIdentifier: '5d46d303-452d-5c9c-ab64-0a9768b87956',
|
||||
objectUniversalIdentifier: '20202020-5a9a-44e8-95df-771cd06d0fb1',
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
'a3b9f10c-f780-58cc-bbc9-402ed5f136f6',
|
||||
relationTargetObjectMetadataUniversalIdentifier:
|
||||
'dfd43356-39b3-4b55-b4a7-279bec689928',
|
||||
type: FieldType.MORPH_RELATION,
|
||||
morphId: '20202020-f636-435d-ab8d-e1168b375c71',
|
||||
},
|
||||
],
|
||||
roles: [
|
||||
{
|
||||
universalIdentifier: '9238bc7b-d38f-4a1c-9d19-31ab7bc67a2f',
|
||||
label: 'Hello world default function role',
|
||||
description: 'Hello world default function role',
|
||||
canReadAllObjectRecords: true,
|
||||
canUpdateAllObjectRecords: true,
|
||||
canSoftDeleteAllObjectRecords: true,
|
||||
canDestroyAllObjectRecords: false,
|
||||
},
|
||||
],
|
||||
skills: [
|
||||
{
|
||||
universalIdentifier: 'd0940029-9d3c-40be-903a-52d65393028f',
|
||||
name: 'example-skill',
|
||||
label: 'Example Skill',
|
||||
description: 'A sample skill for your application',
|
||||
icon: 'IconBrain',
|
||||
content:
|
||||
'Add your skill instructions here. Skills provide context and capabilities to AI agents.',
|
||||
},
|
||||
],
|
||||
agents: [],
|
||||
logicFunctions: [
|
||||
{
|
||||
universalIdentifier: '2baa26eb-9aaf-4856-a4f4-30d6fd6480ee',
|
||||
name: 'hello-world-logic-function',
|
||||
description: 'A simple logic function',
|
||||
timeoutSeconds: 5,
|
||||
httpRouteTriggerSettings: {
|
||||
path: '/hello-world-logic-function',
|
||||
httpMethod: 'GET',
|
||||
isAuthRequired: false,
|
||||
},
|
||||
toolInputSchema: { type: 'object', properties: {} },
|
||||
handlerName: 'default.config.handler',
|
||||
sourceHandlerPath: 'src/logic-functions/hello-world.ts',
|
||||
builtHandlerPath: 'src/logic-functions/hello-world.mjs',
|
||||
builtHandlerChecksum: '[checksum]',
|
||||
},
|
||||
{
|
||||
universalIdentifier: '7a3f4684-51db-494d-833b-a747a3b90507',
|
||||
name: 'post-install',
|
||||
description: 'Runs after installation to set up the application.',
|
||||
timeoutSeconds: 300,
|
||||
toolInputSchema: { type: 'object', properties: {} },
|
||||
handlerName: 'default.config.handler',
|
||||
sourceHandlerPath: 'src/logic-functions/post-install.ts',
|
||||
builtHandlerPath: 'src/logic-functions/post-install.mjs',
|
||||
builtHandlerChecksum: '[checksum]',
|
||||
},
|
||||
{
|
||||
universalIdentifier: '1272ffdb-8e2f-492c-ab37-66c2b97e9c23',
|
||||
name: 'pre-install',
|
||||
description: 'Runs before installation to prepare the application.',
|
||||
timeoutSeconds: 300,
|
||||
toolInputSchema: { type: 'object', properties: {} },
|
||||
handlerName: 'default.config.handler',
|
||||
sourceHandlerPath: 'src/logic-functions/pre-install.ts',
|
||||
builtHandlerPath: 'src/logic-functions/pre-install.mjs',
|
||||
builtHandlerChecksum: '[checksum]',
|
||||
},
|
||||
],
|
||||
frontComponents: [
|
||||
{
|
||||
universalIdentifier: 'd371f098-5b2c-42f0-898d-94459f1ee337',
|
||||
name: 'hello-world-front-component',
|
||||
description: 'A sample front component',
|
||||
componentName: 'HelloWorld',
|
||||
sourceComponentPath: 'src/front-components/hello-world.tsx',
|
||||
builtComponentPath: 'src/front-components/hello-world.mjs',
|
||||
builtComponentChecksum: '[checksum]',
|
||||
isHeadless: false,
|
||||
},
|
||||
],
|
||||
publicAssets: [],
|
||||
views: [
|
||||
{
|
||||
universalIdentifier: 'e004df40-29f3-47ba-b39d-d3a5c444367a',
|
||||
name: 'All example items',
|
||||
objectUniversalIdentifier: 'dfd43356-39b3-4b55-b4a7-279bec689928',
|
||||
icon: 'IconList',
|
||||
key: ViewKey.INDEX,
|
||||
position: 0,
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: '496c40c2-5766-419c-93bf-20fdad3f34bb',
|
||||
fieldMetadataUniversalIdentifier:
|
||||
'd2d7f6cd-33f6-456f-bf00-17adeca926ba',
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 200,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
navigationMenuItems: [
|
||||
{
|
||||
universalIdentifier: '10f90627-e9c2-44b7-9742-bed77e3d1b17',
|
||||
name: 'example-navigation-menu-item',
|
||||
icon: 'IconList',
|
||||
color: 'blue',
|
||||
position: 0,
|
||||
viewUniversalIdentifier: 'e004df40-29f3-47ba-b39d-d3a5c444367a',
|
||||
},
|
||||
],
|
||||
pageLayouts: [],
|
||||
};
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { join } from 'path';
|
||||
|
||||
import { normalizeManifestForComparison } from '@/cli/__tests__/integration/utils/normalize-manifest.util';
|
||||
import { readJson } from '@/cli/utilities/file/fs-utils';
|
||||
import { type Manifest } from 'twenty-shared/application';
|
||||
import { EXPECTED_MANIFEST } from '../expected-manifest';
|
||||
|
||||
export const defineManifestTests = (appPath: string): void => {
|
||||
const manifestOutputPath = join(appPath, '.twenty/output/manifest.json');
|
||||
|
||||
describe('manifest', () => {
|
||||
it('should build manifest matching expected JSON', async () => {
|
||||
const manifest = await readJson<Manifest>(manifestOutputPath);
|
||||
|
||||
expect(manifest).not.toBeNull();
|
||||
|
||||
expect(manifest.objects).toHaveLength(1);
|
||||
expect(manifest.logicFunctions).toHaveLength(3);
|
||||
expect(manifest.frontComponents).toHaveLength(1);
|
||||
expect(manifest.roles).toHaveLength(1);
|
||||
expect(manifest.skills).toHaveLength(1);
|
||||
expect(manifest.fields).toHaveLength(6);
|
||||
expect(manifest.views).toHaveLength(1);
|
||||
expect(manifest.navigationMenuItems).toHaveLength(1);
|
||||
|
||||
expect(normalizeManifestForComparison(manifest)).toEqual(
|
||||
normalizeManifestForComparison(EXPECTED_MANIFEST),
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -2940,6 +2940,8 @@ type Query {
|
||||
currentUser: User!
|
||||
currentWorkspace: Workspace!
|
||||
getPublicWorkspaceDataByDomain(origin: String): PublicWorkspaceData!
|
||||
findManyApplications: [Application!]!
|
||||
findOneApplication(id: UUID, universalIdentifier: UUID): Application!
|
||||
checkUserExists(email: String!, captchaToken: String): CheckUserExist!
|
||||
checkWorkspaceInviteHashIsValid(inviteHash: String!): WorkspaceInviteHashValid!
|
||||
findWorkspaceFromInviteHash(inviteHash: String!): Workspace!
|
||||
@@ -2991,8 +2993,6 @@ type Query {
|
||||
getEmailingDomains: [EmailingDomain!]!
|
||||
findManyMarketplaceApps: [MarketplaceApp!]!
|
||||
findOneMarketplaceApp(universalIdentifier: String!): MarketplaceApp!
|
||||
findManyApplications: [Application!]!
|
||||
findOneApplication(id: UUID, universalIdentifier: UUID): Application!
|
||||
}
|
||||
|
||||
input LogicFunctionIdInput {
|
||||
@@ -3215,6 +3215,10 @@ type Mutation {
|
||||
updateWorkspace(data: UpdateWorkspaceInput!): Workspace!
|
||||
deleteCurrentWorkspace: Workspace!
|
||||
checkCustomDomainValidRecords: DomainValidRecords
|
||||
installApplication(appRegistrationId: String!, version: String): Boolean!
|
||||
runWorkspaceMigration(workspaceMigration: WorkspaceMigrationInput!): Boolean!
|
||||
uninstallApplication(universalIdentifier: String!): Boolean!
|
||||
updateOneApplicationVariable(key: String!, value: String!, applicationId: UUID!): Boolean!
|
||||
getAuthorizationUrlForSSO(input: GetAuthorizationUrlForSSOInput!): GetAuthorizationUrlForSSO!
|
||||
getLoginTokenFromCredentials(email: String!, password: String!, captchaToken: String, locale: String, verifyEmailRedirectPath: String, origin: String!): LoginToken!
|
||||
signIn(email: String!, password: String!, captchaToken: String, locale: String, verifyEmailRedirectPath: String): AvailableWorkspacesAndAccessTokens!
|
||||
@@ -3282,10 +3286,6 @@ type Mutation {
|
||||
verifyEmailingDomain(id: String!): EmailingDomain!
|
||||
createOneAppToken(input: CreateOneAppTokenInput!): AppToken!
|
||||
installMarketplaceApp(universalIdentifier: String!, version: String): Boolean!
|
||||
installApplication(appRegistrationId: String!, version: String): Boolean!
|
||||
runWorkspaceMigration(workspaceMigration: WorkspaceMigrationInput!): Boolean!
|
||||
uninstallApplication(universalIdentifier: String!): Boolean!
|
||||
updateOneApplicationVariable(key: String!, value: String!, applicationId: UUID!): Boolean!
|
||||
createDevelopmentApplication(universalIdentifier: String!, name: String!): DevelopmentApplication!
|
||||
generateApplicationToken(applicationId: UUID!): ApplicationTokenPair!
|
||||
syncApplication(manifest: JSON!): WorkspaceMigration!
|
||||
@@ -4058,6 +4058,49 @@ input UpdateWorkspaceInput {
|
||||
useRecommendedModels: Boolean
|
||||
}
|
||||
|
||||
input WorkspaceMigrationInput {
|
||||
actions: [WorkspaceMigrationDeleteActionInput!]!
|
||||
}
|
||||
|
||||
input WorkspaceMigrationDeleteActionInput {
|
||||
type: WorkspaceMigrationActionType!
|
||||
metadataName: AllMetadataName!
|
||||
universalIdentifier: String!
|
||||
}
|
||||
|
||||
enum WorkspaceMigrationActionType {
|
||||
delete
|
||||
create
|
||||
update
|
||||
}
|
||||
|
||||
enum AllMetadataName {
|
||||
fieldMetadata
|
||||
objectMetadata
|
||||
view
|
||||
viewField
|
||||
viewFieldGroup
|
||||
viewGroup
|
||||
viewSort
|
||||
rowLevelPermissionPredicate
|
||||
rowLevelPermissionPredicateGroup
|
||||
viewFilterGroup
|
||||
index
|
||||
logicFunction
|
||||
viewFilter
|
||||
role
|
||||
roleTarget
|
||||
agent
|
||||
skill
|
||||
pageLayout
|
||||
pageLayoutWidget
|
||||
pageLayoutTab
|
||||
commandMenuItem
|
||||
navigationMenuItem
|
||||
frontComponent
|
||||
webhook
|
||||
}
|
||||
|
||||
input GetAuthorizationUrlForSSOInput {
|
||||
identityProviderId: UUID!
|
||||
workspaceInviteHash: String
|
||||
@@ -4205,49 +4248,6 @@ input CreateAppTokenInput {
|
||||
expiresAt: DateTime!
|
||||
}
|
||||
|
||||
input WorkspaceMigrationInput {
|
||||
actions: [WorkspaceMigrationDeleteActionInput!]!
|
||||
}
|
||||
|
||||
input WorkspaceMigrationDeleteActionInput {
|
||||
type: WorkspaceMigrationActionType!
|
||||
metadataName: AllMetadataName!
|
||||
universalIdentifier: String!
|
||||
}
|
||||
|
||||
enum WorkspaceMigrationActionType {
|
||||
delete
|
||||
create
|
||||
update
|
||||
}
|
||||
|
||||
enum AllMetadataName {
|
||||
fieldMetadata
|
||||
objectMetadata
|
||||
view
|
||||
viewField
|
||||
viewFieldGroup
|
||||
viewGroup
|
||||
viewSort
|
||||
rowLevelPermissionPredicate
|
||||
rowLevelPermissionPredicateGroup
|
||||
viewFilterGroup
|
||||
index
|
||||
logicFunction
|
||||
viewFilter
|
||||
role
|
||||
roleTarget
|
||||
agent
|
||||
skill
|
||||
pageLayout
|
||||
pageLayoutWidget
|
||||
pageLayoutTab
|
||||
commandMenuItem
|
||||
navigationMenuItem
|
||||
frontComponent
|
||||
webhook
|
||||
}
|
||||
|
||||
enum FileFolder {
|
||||
ProfilePicture
|
||||
WorkspaceLogo
|
||||
|
||||
@@ -10584,13 +10584,13 @@ export default {
|
||||
},
|
||||
"EmailAccountConnectionParameters": {
|
||||
"IMAP": [
|
||||
445
|
||||
449
|
||||
],
|
||||
"SMTP": [
|
||||
445
|
||||
449
|
||||
],
|
||||
"CALDAV": [
|
||||
445
|
||||
449
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -10629,7 +10629,7 @@ export default {
|
||||
},
|
||||
"CreateOneAppTokenInput": {
|
||||
"appToken": [
|
||||
448
|
||||
452
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -10643,30 +10643,6 @@ export default {
|
||||
1
|
||||
]
|
||||
},
|
||||
"WorkspaceMigrationInput": {
|
||||
"actions": [
|
||||
450
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"WorkspaceMigrationDeleteActionInput": {
|
||||
"type": [
|
||||
451
|
||||
],
|
||||
"metadataName": [
|
||||
452
|
||||
],
|
||||
"universalIdentifier": [
|
||||
1
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"WorkspaceMigrationActionType": {},
|
||||
"AllMetadataName": {},
|
||||
"FileFolder": {},
|
||||
"Subscription": {
|
||||
"onDbEvent": [
|
||||
|
||||
@@ -247,6 +247,17 @@
|
||||
"nx command-no-deps -- workspace:seed:dev"
|
||||
],
|
||||
"parallel": false
|
||||
},
|
||||
"with-apps": {
|
||||
"cwd": "packages/twenty-server",
|
||||
"commands": [
|
||||
"nx ts-node-no-deps-transpile-only -- ./scripts/truncate-db.ts",
|
||||
"nx ts-node-no-deps-transpile-only -- ./scripts/setup-db.ts",
|
||||
"nx database:migrate",
|
||||
"nx command-no-deps -- cache:flush",
|
||||
"nx command-no-deps -- workspace:seed:dev --with-apps"
|
||||
],
|
||||
"parallel": false
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "seed"
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
|
||||
import { Command, CommandRunner } from 'nest-commander';
|
||||
import { Command, CommandRunner, Option } from 'nest-commander';
|
||||
|
||||
import {
|
||||
SEED_APPLE_WORKSPACE_ID,
|
||||
SEED_YCOMBINATOR_WORKSPACE_ID,
|
||||
} from 'src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant';
|
||||
import { DevSeederService } from 'src/engine/workspace-manager/dev-seeder/services/dev-seeder.service';
|
||||
|
||||
type SeedDevOptions = {
|
||||
withApps?: boolean;
|
||||
};
|
||||
|
||||
@Command({
|
||||
name: 'workspace:seed:dev',
|
||||
description:
|
||||
@@ -23,10 +28,21 @@ export class DataSeedWorkspaceCommand extends CommandRunner {
|
||||
super();
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
@Option({
|
||||
flags: '--with-apps',
|
||||
description: 'Include fixture app seeding (builds and installs apps)',
|
||||
required: false,
|
||||
})
|
||||
parseWithApps(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
async run(_passedParams: string[], options: SeedDevOptions): Promise<void> {
|
||||
try {
|
||||
for (const workspaceId of this.workspaceIds) {
|
||||
await this.devSeederService.seedDev(workspaceId);
|
||||
await this.devSeederService.seedDev(workspaceId, {
|
||||
includeApps: options?.withApps ?? false,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
|
||||
+118
-20
@@ -4,6 +4,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { promises as fs } from 'fs';
|
||||
import { join, relative } from 'path';
|
||||
|
||||
import { type Manifest } from 'twenty-shared/application';
|
||||
import { FileFolder } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
@@ -94,6 +95,55 @@ export class ApplicationInstallService {
|
||||
);
|
||||
}
|
||||
|
||||
async installFromFixtureDirectory(params: {
|
||||
appRegistrationId: string;
|
||||
fixtureDir: string;
|
||||
workspaceId: string;
|
||||
}): Promise<boolean> {
|
||||
const appRegistration = await this.appRegistrationRepository.findOne({
|
||||
where: { id: params.appRegistrationId },
|
||||
});
|
||||
|
||||
if (!appRegistration) {
|
||||
throw new ApplicationException(
|
||||
`Application registration with id ${params.appRegistrationId} not found`,
|
||||
ApplicationExceptionCode.APPLICATION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const manifestContent = await fs.readFile(
|
||||
join(params.fixtureDir, 'manifest.json'),
|
||||
'utf-8',
|
||||
);
|
||||
const manifest: Manifest = JSON.parse(manifestContent);
|
||||
|
||||
await this.ensureApplicationExists({
|
||||
universalIdentifier: appRegistration.universalIdentifier,
|
||||
name: manifest.application.displayName,
|
||||
workspaceId: params.workspaceId,
|
||||
applicationRegistrationId: appRegistration.id,
|
||||
sourceType: appRegistration.sourceType,
|
||||
});
|
||||
|
||||
await this.writeDependencyFilesToStorage(
|
||||
params.fixtureDir,
|
||||
appRegistration.universalIdentifier,
|
||||
params.workspaceId,
|
||||
);
|
||||
|
||||
await this.applicationSyncService.synchronizeFromManifest({
|
||||
workspaceId: params.workspaceId,
|
||||
manifest,
|
||||
applicationRegistrationId: appRegistration.id,
|
||||
});
|
||||
|
||||
this.logger.log(
|
||||
`Installed app from fixture directory: ${appRegistration.universalIdentifier}`,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async doInstallApplication(
|
||||
appRegistration: ApplicationRegistrationEntity,
|
||||
params: { version?: string; workspaceId: string },
|
||||
@@ -111,30 +161,15 @@ export class ApplicationInstallService {
|
||||
return true;
|
||||
}
|
||||
|
||||
const universalIdentifier = appRegistration.universalIdentifier;
|
||||
|
||||
await this.ensureApplicationExists({
|
||||
universalIdentifier,
|
||||
name: resolvedPackage.manifest.application.displayName,
|
||||
workspaceId: params.workspaceId,
|
||||
applicationRegistrationId: appRegistration.id,
|
||||
sourceType: appRegistration.sourceType,
|
||||
});
|
||||
|
||||
await this.writeFilesToStorage(
|
||||
resolvedPackage.extractedDir,
|
||||
universalIdentifier,
|
||||
params.workspaceId,
|
||||
);
|
||||
|
||||
await this.applicationSyncService.synchronizeFromManifest({
|
||||
workspaceId: params.workspaceId,
|
||||
await this.installFromExtractedDir({
|
||||
appRegistration,
|
||||
extractedDir: resolvedPackage.extractedDir,
|
||||
manifest: resolvedPackage.manifest,
|
||||
applicationRegistrationId: appRegistration.id,
|
||||
workspaceId: params.workspaceId,
|
||||
});
|
||||
|
||||
this.logger.log(
|
||||
`Successfully installed app ${universalIdentifier} v${resolvedPackage.packageJson.version ?? 'unknown'}`,
|
||||
`Successfully installed app ${appRegistration.universalIdentifier} v${resolvedPackage.packageJson.version ?? 'unknown'}`,
|
||||
);
|
||||
|
||||
return true;
|
||||
@@ -153,6 +188,69 @@ export class ApplicationInstallService {
|
||||
}
|
||||
}
|
||||
|
||||
private async installFromExtractedDir(params: {
|
||||
appRegistration: ApplicationRegistrationEntity;
|
||||
extractedDir: string;
|
||||
manifest: Manifest;
|
||||
workspaceId: string;
|
||||
}): Promise<void> {
|
||||
await this.ensureApplicationExists({
|
||||
universalIdentifier: params.appRegistration.universalIdentifier,
|
||||
name: params.manifest.application.displayName,
|
||||
workspaceId: params.workspaceId,
|
||||
applicationRegistrationId: params.appRegistration.id,
|
||||
sourceType: params.appRegistration.sourceType,
|
||||
});
|
||||
|
||||
await this.writeFilesToStorage(
|
||||
params.extractedDir,
|
||||
params.appRegistration.universalIdentifier,
|
||||
params.workspaceId,
|
||||
);
|
||||
|
||||
await this.applicationSyncService.synchronizeFromManifest({
|
||||
workspaceId: params.workspaceId,
|
||||
manifest: params.manifest,
|
||||
applicationRegistrationId: params.appRegistration.id,
|
||||
});
|
||||
}
|
||||
|
||||
private async writeDependencyFilesToStorage(
|
||||
fixtureDir: string,
|
||||
applicationUniversalIdentifier: string,
|
||||
workspaceId: string,
|
||||
): Promise<void> {
|
||||
const dependencyFiles = ['package.json', 'yarn.lock'];
|
||||
|
||||
for (const fileName of dependencyFiles) {
|
||||
const filePath = join(fixtureDir, fileName);
|
||||
|
||||
try {
|
||||
const content = await fs.readFile(filePath);
|
||||
|
||||
await this.fileStorageService.writeFile({
|
||||
sourceFile: content,
|
||||
mimeType: undefined,
|
||||
fileFolder: FileFolder.Dependencies,
|
||||
applicationUniversalIdentifier,
|
||||
workspaceId,
|
||||
resourcePath: fileName,
|
||||
settings: { isTemporaryFile: false, toDelete: false },
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
const isFileNotFound =
|
||||
typeof error === 'object' &&
|
||||
error !== null &&
|
||||
'code' in error &&
|
||||
(error as NodeJS.ErrnoException).code === 'ENOENT';
|
||||
|
||||
if (!isFileNotFound) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async writeFilesToStorage(
|
||||
extractedDir: string,
|
||||
applicationUniversalIdentifier: string,
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import { join } from 'path';
|
||||
|
||||
import { ApplicationRegistrationSourceType } from 'src/engine/core-modules/application/application-registration/enums/application-registration-source-type.enum';
|
||||
|
||||
// In Docker the server cwd is /app/packages/twenty-server,
|
||||
// so ../twenty-apps/fixtures resolves to /app/packages/twenty-apps/fixtures.
|
||||
const FIXTURES_ROOT = join(process.cwd(), '..', 'twenty-apps', 'fixtures');
|
||||
|
||||
export type AppSeedDefinition = {
|
||||
registration: {
|
||||
universalIdentifier: string;
|
||||
name: string;
|
||||
description: string;
|
||||
sourceType: ApplicationRegistrationSourceType;
|
||||
sourcePackage?: string;
|
||||
author?: string;
|
||||
};
|
||||
fixtureDir: string;
|
||||
};
|
||||
|
||||
export const APP_SEEDS: AppSeedDefinition[] = [
|
||||
{
|
||||
registration: {
|
||||
universalIdentifier: '6563e091-9f5b-4026-a3ea-7e3b3d09e218',
|
||||
name: 'Hello World',
|
||||
description: 'A sample marketplace app installed via NPM',
|
||||
sourceType: ApplicationRegistrationSourceType.NPM,
|
||||
sourcePackage: 'hello-world',
|
||||
author: 'Twenty',
|
||||
},
|
||||
fixtureDir: join(FIXTURES_ROOT, 'hello-world-app'),
|
||||
},
|
||||
{
|
||||
registration: {
|
||||
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
|
||||
name: 'Postcard App',
|
||||
description: 'An internal app installed as tarball',
|
||||
sourceType: ApplicationRegistrationSourceType.TARBALL,
|
||||
author: 'Twenty',
|
||||
},
|
||||
fixtureDir: join(FIXTURES_ROOT, 'postcard-app'),
|
||||
},
|
||||
{
|
||||
registration: {
|
||||
universalIdentifier: 'e1e2e3e4-e5e6-4000-8000-000000000001',
|
||||
name: 'Minimal App',
|
||||
description: 'A dev-mode app running locally',
|
||||
sourceType: ApplicationRegistrationSourceType.LOCAL,
|
||||
},
|
||||
fixtureDir: join(FIXTURES_ROOT, 'minimal-app'),
|
||||
},
|
||||
];
|
||||
@@ -3,6 +3,8 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||
import { ApiKeyModule } from 'src/engine/core-modules/api-key/api-key.module';
|
||||
import { ApplicationInstallModule } from 'src/engine/core-modules/application/application-install/application-install.module';
|
||||
import { ApplicationRegistrationEntity } from 'src/engine/core-modules/application/application-registration/application-registration.entity';
|
||||
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { FileStorageModule } from 'src/engine/core-modules/file-storage/file-storage.module';
|
||||
@@ -42,7 +44,12 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace
|
||||
ApplicationModule,
|
||||
FeatureFlagModule,
|
||||
FileStorageModule,
|
||||
TypeOrmModule.forFeature([WorkspaceEntity, ObjectMetadataEntity]),
|
||||
TypeOrmModule.forFeature([
|
||||
WorkspaceEntity,
|
||||
ObjectMetadataEntity,
|
||||
ApplicationRegistrationEntity,
|
||||
]),
|
||||
ApplicationInstallModule,
|
||||
ObjectPermissionModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
WorkspaceCacheModule,
|
||||
|
||||
+74
-4
@@ -1,10 +1,12 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectDataSource } from '@nestjs/typeorm';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { ALL_METADATA_NAME } from 'twenty-shared/metadata';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { FeatureFlagKey } from 'twenty-shared/types';
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
|
||||
import { ApplicationInstallService } from 'src/engine/core-modules/application/application-install/application-install.service';
|
||||
import { ApplicationRegistrationEntity } from 'src/engine/core-modules/application/application-registration/application-registration.entity';
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
@@ -14,6 +16,10 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
import {
|
||||
APP_SEEDS,
|
||||
type AppSeedDefinition,
|
||||
} from 'src/engine/workspace-manager/dev-seeder/core/constants/app-seeds.constant';
|
||||
import { SeededWorkspacesIds } from 'src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant';
|
||||
import { DevSeederPermissionsService } from 'src/engine/workspace-manager/dev-seeder/core/services/dev-seeder-permissions.service';
|
||||
import { seedCoreSchema } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-core-schema.util';
|
||||
@@ -27,6 +33,8 @@ import { TwentyStandardApplicationService } from 'src/engine/workspace-manager/t
|
||||
|
||||
@Injectable()
|
||||
export class DevSeederService {
|
||||
private readonly logger = new Logger(DevSeederService.name);
|
||||
|
||||
constructor(
|
||||
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
||||
private readonly twentyConfigService: TwentyConfigService,
|
||||
@@ -37,12 +45,18 @@ export class DevSeederService {
|
||||
private readonly devSeederPermissionsService: DevSeederPermissionsService,
|
||||
private readonly devSeederDataService: DevSeederDataService,
|
||||
private readonly applicationService: ApplicationService,
|
||||
private readonly applicationInstallService: ApplicationInstallService,
|
||||
private readonly workspaceCacheService: WorkspaceCacheService,
|
||||
@InjectRepository(ApplicationRegistrationEntity)
|
||||
private readonly appRegistrationRepository: Repository<ApplicationRegistrationEntity>,
|
||||
@InjectDataSource()
|
||||
private readonly coreDataSource: DataSource,
|
||||
) {}
|
||||
|
||||
public async seedDev(workspaceId: SeededWorkspacesIds): Promise<void> {
|
||||
public async seedDev(
|
||||
workspaceId: SeededWorkspacesIds,
|
||||
options: { includeApps?: boolean } = {},
|
||||
): Promise<void> {
|
||||
const isBillingEnabled = this.twentyConfigService.get('IS_BILLING_ENABLED');
|
||||
const appVersion = this.twentyConfigService.get('APP_VERSION');
|
||||
|
||||
@@ -83,6 +97,10 @@ export class DevSeederService {
|
||||
},
|
||||
);
|
||||
|
||||
if (options.includeApps) {
|
||||
await this.seedApps(workspaceId);
|
||||
}
|
||||
|
||||
await this.devSeederMetadataService.seed({
|
||||
dataSourceMetadata,
|
||||
workspaceId,
|
||||
@@ -166,4 +184,56 @@ export class DevSeederService {
|
||||
|
||||
await this.workspaceCacheStorageService.flush(workspaceId, undefined);
|
||||
}
|
||||
|
||||
private async seedApps(workspaceId: string): Promise<void> {
|
||||
for (const seed of APP_SEEDS) {
|
||||
try {
|
||||
await this.seedOneApp(seed, workspaceId);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to seed app "${seed.registration.name}": ${error}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async seedOneApp(
|
||||
seed: AppSeedDefinition,
|
||||
workspaceId: string,
|
||||
): Promise<void> {
|
||||
let registration = await this.appRegistrationRepository.findOne({
|
||||
where: {
|
||||
universalIdentifier: seed.registration.universalIdentifier,
|
||||
},
|
||||
});
|
||||
|
||||
if (!registration) {
|
||||
registration = await this.appRegistrationRepository.save(
|
||||
this.appRegistrationRepository.create({
|
||||
universalIdentifier: seed.registration.universalIdentifier,
|
||||
name: seed.registration.name,
|
||||
description: seed.registration.description,
|
||||
sourceType: seed.registration.sourceType,
|
||||
sourcePackage: seed.registration.sourcePackage,
|
||||
author: seed.registration.author,
|
||||
oAuthClientId: `20202020-seed-${seed.registration.universalIdentifier.slice(0, 8)}`,
|
||||
oAuthClientSecretHash: 'seed',
|
||||
oAuthRedirectUris: [],
|
||||
oAuthScopes: [],
|
||||
ownerWorkspaceId: workspaceId,
|
||||
createdByUserId: null,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
await this.applicationInstallService.installFromFixtureDirectory({
|
||||
appRegistrationId: registration.id,
|
||||
fixtureDir: seed.fixtureDir,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
this.logger.log(
|
||||
`Seeded app "${seed.registration.name}" (${seed.registration.sourceType})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user