Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
197f8f48d5 |
+1
-2
@@ -34,8 +34,7 @@
|
||||
"@types/qs": "6.9.16",
|
||||
"@wyw-in-js/transform@npm:0.6.0": "patch:@wyw-in-js/transform@npm%3A0.7.0#~/.yarn/patches/@wyw-in-js-transform-npm-0.7.0-ba641dc99f.patch",
|
||||
"@wyw-in-js/transform@npm:0.7.0": "patch:@wyw-in-js/transform@npm%3A0.7.0#~/.yarn/patches/@wyw-in-js-transform-npm-0.7.0-ba641dc99f.patch",
|
||||
"@opentelemetry/api": "1.9.1",
|
||||
"chokidar": "^3.6.0"
|
||||
"@opentelemetry/api": "1.9.1"
|
||||
},
|
||||
"version": "0.2.1",
|
||||
"nx": {},
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
"test:watch": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"twenty-client-sdk": "2.2.0",
|
||||
"twenty-sdk": "2.2.0"
|
||||
"twenty-client-sdk": "0.9.0",
|
||||
"twenty-sdk": "0.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.7.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import { useRecordId } from 'twenty-sdk/front-component';
|
||||
import { useSelectedRecordIds } from 'twenty-sdk/front-component';
|
||||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -72,7 +72,8 @@ const CardDisplay = ({
|
||||
};
|
||||
|
||||
const PostCardPreview = () => {
|
||||
const recordId = useRecordId();
|
||||
const selectedRecordIds = useSelectedRecordIds();
|
||||
const recordId = selectedRecordIds.length === 1 ? selectedRecordIds[0] : null;
|
||||
const [postCard, setPostCard] = useState<PostCardRecord | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
+3
-7
@@ -1,11 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import {
|
||||
enqueueSnackbar,
|
||||
unmountFrontComponent,
|
||||
updateProgress,
|
||||
useRecordId,
|
||||
} from 'twenty-sdk/front-component';
|
||||
import { useSelectedRecordIds, updateProgress, enqueueSnackbar, unmountFrontComponent } from 'twenty-sdk/front-component';
|
||||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||||
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
|
||||
|
||||
@@ -15,7 +10,8 @@ const SYSTEM_PROMPT =
|
||||
'text, nothing else — no greeting label, no sign-off label, just the message.';
|
||||
|
||||
const GeneratePostCardEffect = () => {
|
||||
const recordId = useRecordId();
|
||||
const selectedRecordIds = useSelectedRecordIds();
|
||||
const recordId = selectedRecordIds.length === 1 ? selectedRecordIds[0] : null;
|
||||
|
||||
useEffect(() => {
|
||||
if (recordId === null) {
|
||||
|
||||
+22
-14
@@ -1,42 +1,50 @@
|
||||
import { useEffect } from 'react';
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import {
|
||||
enqueueSnackbar,
|
||||
unmountFrontComponent,
|
||||
updateProgress,
|
||||
useRecordId,
|
||||
} from 'twenty-sdk/front-component';
|
||||
import { useSelectedRecordIds, updateProgress, enqueueSnackbar, unmountFrontComponent } from 'twenty-sdk/front-component';
|
||||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||||
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
|
||||
|
||||
const SendPostCardsEffect = () => {
|
||||
const recordId = useRecordId();
|
||||
const selectedRecordIds = useSelectedRecordIds();
|
||||
|
||||
useEffect(() => {
|
||||
const send = async () => {
|
||||
try {
|
||||
if (selectedRecordIds.length === 0) {
|
||||
await enqueueSnackbar({
|
||||
message: 'No postcards selected',
|
||||
variant: 'error',
|
||||
});
|
||||
await unmountFrontComponent();
|
||||
return;
|
||||
}
|
||||
|
||||
await updateProgress(0.1);
|
||||
const client = new CoreApiClient();
|
||||
|
||||
await updateProgress(0.3);
|
||||
|
||||
if (recordId) {
|
||||
for (let i = 0; i < selectedRecordIds.length; i++) {
|
||||
await client.mutation({
|
||||
updatePostCard: {
|
||||
__args: {
|
||||
id: recordId,
|
||||
id: selectedRecordIds[i],
|
||||
data: { status: 'SENT' },
|
||||
},
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
await enqueueSnackbar({
|
||||
message: `Postcard sent`,
|
||||
variant: 'success',
|
||||
});
|
||||
await updateProgress(0.3 + (0.7 * (i + 1)) / selectedRecordIds.length);
|
||||
}
|
||||
|
||||
const count = selectedRecordIds.length;
|
||||
|
||||
await enqueueSnackbar({
|
||||
message: `${count} postcard${count > 1 ? 's' : ''} sent`,
|
||||
variant: 'success',
|
||||
});
|
||||
|
||||
await unmountFrontComponent();
|
||||
} catch (error) {
|
||||
const message =
|
||||
@@ -48,7 +56,7 @@ const SendPostCardsEffect = () => {
|
||||
};
|
||||
|
||||
send();
|
||||
}, [recordId]);
|
||||
}, [selectedRecordIds]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -3317,8 +3317,8 @@ __metadata:
|
||||
oxlint: "npm:^0.16.0"
|
||||
react: "npm:^19.0.0"
|
||||
react-dom: "npm:^19.0.0"
|
||||
twenty-client-sdk: "npm:2.2.0"
|
||||
twenty-sdk: "npm:2.2.0"
|
||||
twenty-client-sdk: "npm:0.9.0"
|
||||
twenty-sdk: "npm:0.9.0"
|
||||
typescript: "npm:^5.9.3"
|
||||
vite-tsconfig-paths: "npm:^4.2.1"
|
||||
vitest: "npm:^3.1.1"
|
||||
@@ -4059,21 +4059,21 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"twenty-client-sdk@npm:2.2.0":
|
||||
version: 2.2.0
|
||||
resolution: "twenty-client-sdk@npm:2.2.0"
|
||||
"twenty-client-sdk@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "twenty-client-sdk@npm:0.9.0"
|
||||
dependencies:
|
||||
"@genql/cli": "npm:^3.0.3"
|
||||
"@genql/runtime": "npm:^2.10.0"
|
||||
esbuild: "npm:^0.25.0"
|
||||
graphql: "npm:^16.8.1"
|
||||
checksum: 10c0/90122593efa53440ae386960211a2c274b13a410942bf6f9bc35cf952ae55fe83280e29074677fd284fa3c79069fc578980db06a92a143c08e043f865dbbbd3c
|
||||
checksum: 10c0/4b42a6622a9852fc3eca50c1131b116c5602af006ee5f1d3b4f1e97721bbc8ef5c2f3b60d9dee3ca9ca8c3c7ad4b292a7b55007b779b4c042997dbc29940ba54
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"twenty-sdk@npm:2.2.0":
|
||||
version: 2.2.0
|
||||
resolution: "twenty-sdk@npm:2.2.0"
|
||||
"twenty-sdk@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "twenty-sdk@npm:0.9.0"
|
||||
dependencies:
|
||||
"@genql/cli": "npm:^3.0.3"
|
||||
"@genql/runtime": "npm:^2.10.0"
|
||||
@@ -4093,7 +4093,7 @@ __metadata:
|
||||
react: "npm:^19.0.0"
|
||||
react-dom: "npm:^19.0.0"
|
||||
tinyglobby: "npm:^0.2.15"
|
||||
twenty-client-sdk: "npm:2.2.0"
|
||||
twenty-client-sdk: "npm:0.9.0"
|
||||
typescript: "npm:^5.9.2"
|
||||
uuid: "npm:^13.0.0"
|
||||
vite: "npm:^7.0.0"
|
||||
@@ -4101,7 +4101,7 @@ __metadata:
|
||||
zod: "npm:^4.1.11"
|
||||
bin:
|
||||
twenty: dist/cli.cjs
|
||||
checksum: 10c0/8978b4b0aa5ea282c8f76799347d9d1812901ec609bc2bd9270261a6bc5589ad361f6102a06900a4511a29a8378cd3811c2c0bad9f01cb8adadabca6d96dfd0b
|
||||
checksum: 10c0/27f93e5edac3265f819abacc853598435718020258a95d55518562e086e42daabae784964b42005d8e4ff30d1d6f13a12a38c656e18c60bad98da3f579a8e1c3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
@@ -2872,7 +2872,6 @@ enum AllMetadataName {
|
||||
fieldPermission
|
||||
frontComponent
|
||||
webhook
|
||||
applicationVariable
|
||||
connectionProvider
|
||||
}
|
||||
|
||||
|
||||
@@ -2495,7 +2495,7 @@ export interface CollectionHash {
|
||||
__typename: 'CollectionHash'
|
||||
}
|
||||
|
||||
export type AllMetadataName = 'fieldMetadata' | 'objectMetadata' | 'view' | 'viewField' | 'viewFieldGroup' | 'viewGroup' | 'viewSort' | 'rowLevelPermissionPredicate' | 'rowLevelPermissionPredicateGroup' | 'viewFilterGroup' | 'index' | 'logicFunction' | 'viewFilter' | 'role' | 'roleTarget' | 'agent' | 'skill' | 'pageLayout' | 'pageLayoutWidget' | 'pageLayoutTab' | 'commandMenuItem' | 'navigationMenuItem' | 'permissionFlag' | 'objectPermission' | 'fieldPermission' | 'frontComponent' | 'webhook' | 'applicationVariable' | 'connectionProvider'
|
||||
export type AllMetadataName = 'fieldMetadata' | 'objectMetadata' | 'view' | 'viewField' | 'viewFieldGroup' | 'viewGroup' | 'viewSort' | 'rowLevelPermissionPredicate' | 'rowLevelPermissionPredicateGroup' | 'viewFilterGroup' | 'index' | 'logicFunction' | 'viewFilter' | 'role' | 'roleTarget' | 'agent' | 'skill' | 'pageLayout' | 'pageLayoutWidget' | 'pageLayoutTab' | 'commandMenuItem' | 'navigationMenuItem' | 'permissionFlag' | 'objectPermission' | 'fieldPermission' | 'frontComponent' | 'webhook' | 'connectionProvider'
|
||||
|
||||
export interface MinimalObjectMetadata {
|
||||
id: Scalars['UUID']
|
||||
@@ -8854,7 +8854,6 @@ export const enumAllMetadataName = {
|
||||
fieldPermission: 'fieldPermission' as const,
|
||||
frontComponent: 'frontComponent' as const,
|
||||
webhook: 'webhook' as const,
|
||||
applicationVariable: 'applicationVariable' as const,
|
||||
connectionProvider: 'connectionProvider' as const
|
||||
}
|
||||
|
||||
|
||||
@@ -185,7 +185,6 @@ export type AiSystemPromptSection = {
|
||||
|
||||
export enum AllMetadataName {
|
||||
agent = 'agent',
|
||||
applicationVariable = 'applicationVariable',
|
||||
commandMenuItem = 'commandMenuItem',
|
||||
connectionProvider = 'connectionProvider',
|
||||
fieldMetadata = 'fieldMetadata',
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Toepassing suksesvol opgegradeer."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "تمت ترقية التطبيق بنجاح."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Aplicació actualitzada correctament."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Aplikace byla úspěšně aktualizována."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Applikationen blev opgraderet med succes."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr "Anwendung erfolgreich deinstalliert."
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Anwendung erfolgreich aktualisiert."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Η εφαρμογή αναβαθμίστηκε με επιτυχία."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2162,11 +2162,6 @@ msgstr "Application successfully uninstalled."
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Application upgraded successfully."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr "application variable"
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Aplicación actualizada con éxito."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Sovellus päivitetty onnistuneesti."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Application mise à niveau avec succès."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "היישום שודרג בהצלחה."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Az alkalmazás sikeresen frissítve."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Applicazione aggiornata con successo."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "アプリケーションのアップグレードに成功しました。"
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "응용 프로그램이 성공적으로 업그레이드되었습니다."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Applicatie succesvol geüpgraded."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Applikasjonen ble oppgradert vellykket."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Aplikacja została pomyślnie zaktualizowana."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2162,11 +2162,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr "Aplicação desinstalada com sucesso."
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Aplicação atualizada com sucesso."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Aplicação atualizada com sucesso."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Aplicația a fost actualizată cu succes."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
Binary file not shown.
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Апликација је успешно надограђена."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Applikationen uppgraderades framgångsrikt."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Uygulama başarıyla yükseltildi."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Застосунок успішно оновлено."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "Đã nâng cấp ứng dụng thành công."
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "应用程序升级成功。"
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
@@ -2167,11 +2167,6 @@ msgstr ""
|
||||
msgid "Application upgraded successfully."
|
||||
msgstr "應用程式升級成功。"
|
||||
|
||||
#. js-lingui-id: Okob8q
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "application variable"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: fL7WXr
|
||||
#: src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx
|
||||
#: src/pages/settings/layout/components/SettingsLayoutDetailScaffold.tsx
|
||||
|
||||
-1
@@ -48,7 +48,6 @@ export const useMetadataErrorHandler = () => {
|
||||
navigationMenuItem: t`navigation menu item`,
|
||||
webhook: t`webhook`,
|
||||
viewSort: t`view sort`,
|
||||
applicationVariable: t`application variable`,
|
||||
connectionProvider: t`connection provider`,
|
||||
} as const satisfies Record<AllMetadataName, string>;
|
||||
|
||||
|
||||
+24
-14
@@ -22,8 +22,16 @@ const RichTextFieldEditor = lazy(() =>
|
||||
);
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const StyledContent = styled.div`
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
margin: ${themeCssVariables.spacing[4]} -8px;
|
||||
overflow-y: auto;
|
||||
padding-inline: 44px 0px;
|
||||
width: 100%;
|
||||
`;
|
||||
@@ -56,20 +64,22 @@ export const SidePanelEditRichTextPage = () => {
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<Suspense fallback={<LoadingSkeleton />}>
|
||||
{isActivityObject(objectNameSingular) ? (
|
||||
<ActivityRichTextEditor
|
||||
activityId={recordId}
|
||||
activityObjectNameSingular={objectNameSingular}
|
||||
/>
|
||||
) : (
|
||||
<RichTextFieldEditor
|
||||
recordId={recordId}
|
||||
objectNameSingular={objectNameSingular}
|
||||
fieldName={fieldName}
|
||||
/>
|
||||
)}
|
||||
</Suspense>
|
||||
<StyledContent>
|
||||
<Suspense fallback={<LoadingSkeleton />}>
|
||||
{isActivityObject(objectNameSingular) ? (
|
||||
<ActivityRichTextEditor
|
||||
activityId={recordId}
|
||||
activityObjectNameSingular={objectNameSingular}
|
||||
/>
|
||||
) : (
|
||||
<RichTextFieldEditor
|
||||
recordId={recordId}
|
||||
objectNameSingular={objectNameSingular}
|
||||
fieldName={fieldName}
|
||||
/>
|
||||
)}
|
||||
</Suspense>
|
||||
</StyledContent>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -76,14 +76,3 @@ export type FunctionExecutionResult = {
|
||||
stackTrace: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type ChokidarFsEvent =
|
||||
| 'add'
|
||||
| 'addDir'
|
||||
| 'change'
|
||||
| 'unlink'
|
||||
| 'unlinkDir'
|
||||
| 'ready'
|
||||
| 'raw'
|
||||
| 'error'
|
||||
| 'all';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import path, { relative } from 'path';
|
||||
import chokidar, { type FSWatcher } from 'chokidar';
|
||||
import { type EventName } from 'chokidar/handler.js';
|
||||
import { ASSETS_DIR } from 'twenty-shared/application';
|
||||
import { type ChokidarFsEvent } from '@/cli/types';
|
||||
|
||||
export type ManifestWatcherOptions = {
|
||||
appPath: string;
|
||||
@@ -13,10 +13,7 @@ const IGNORED_DIRECTORY_NAMES = new Set(['node_modules', 'dist', '.twenty']);
|
||||
|
||||
export class ManifestWatcher {
|
||||
private appPath: string;
|
||||
private handleChangeDetected: (
|
||||
filePath: string,
|
||||
event: ChokidarFsEvent,
|
||||
) => void;
|
||||
private handleChangeDetected: (filePath: string, event: EventName) => void;
|
||||
private verbose: boolean;
|
||||
private watcher: FSWatcher | null = null;
|
||||
|
||||
|
||||
+2
-5
@@ -10,7 +10,7 @@ import { type ManifestBuildResult } from '@/cli/utilities/build/manifest/manifes
|
||||
import { ManifestWatcher } from '@/cli/utilities/build/manifest/manifest-watcher';
|
||||
import { type OrchestratorState } from '@/cli/utilities/dev/orchestrator/dev-mode-orchestrator-state';
|
||||
import type { Location } from 'esbuild';
|
||||
import { type ChokidarFsEvent } from '@/cli/types';
|
||||
import { type EventName } from 'chokidar/handler.js';
|
||||
import { ASSETS_DIR } from 'twenty-shared/application';
|
||||
import { FileFolder } from 'twenty-shared/types';
|
||||
|
||||
@@ -102,10 +102,7 @@ export class StartWatchersOrchestratorStep {
|
||||
]);
|
||||
}
|
||||
|
||||
private handleChangeDetected(
|
||||
sourcePath: string,
|
||||
event: ChokidarFsEvent,
|
||||
): void {
|
||||
private handleChangeDetected(sourcePath: string, event: EventName): void {
|
||||
this.state.addEvent({
|
||||
message: `Change detected: ${sourcePath}`,
|
||||
status: 'info',
|
||||
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
@RegisteredInstanceCommand('2.3.0', 1777966965587)
|
||||
export class TransformApplicationVariableToSyncableEntityFastInstanceCommand
|
||||
implements FastInstanceCommand
|
||||
{
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'DROP INDEX "core"."IDX_78ae6cfe5f49a76c4bf842ad58"',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."applicationVariable" DROP CONSTRAINT "IDX_APPLICATION_VARIABLE_KEY_APPLICATION_ID_UNIQUE"',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."applicationVariable" ADD "universalIdentifier" uuid',
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."applicationVariable" DROP COLUMN "universalIdentifier"',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."applicationVariable" ADD CONSTRAINT "IDX_APPLICATION_VARIABLE_KEY_APPLICATION_ID_UNIQUE" UNIQUE ("key", "applicationId")',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'CREATE INDEX "IDX_78ae6cfe5f49a76c4bf842ad58" ON "core"."applicationVariable" ("workspaceId") ',
|
||||
);
|
||||
}
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
import { DataSource, QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { SlowInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/slow-instance-command.interface';
|
||||
|
||||
@RegisteredInstanceCommand('2.3.0', 1777966965588, { type: 'slow' })
|
||||
export class BackfillApplicationVariableUniversalIdentifierSlowInstanceCommand
|
||||
implements SlowInstanceCommand
|
||||
{
|
||||
async runDataMigration(dataSource: DataSource): Promise<void> {
|
||||
await dataSource.query(
|
||||
'DELETE "core"."applicationVariable" WHERE "applicationId" IS NULL',
|
||||
);
|
||||
await dataSource.query(
|
||||
'UPDATE "core"."applicationVariable" SET "universalIdentifier" = gen_random_uuid() WHERE "universalIdentifier" IS NULL',
|
||||
);
|
||||
}
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."applicationVariable" DROP CONSTRAINT "FK_51adb49e7f8df35dd23e01c4830"',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."applicationVariable" ALTER COLUMN "applicationId" SET NOT NULL',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."applicationVariable" ADD CONSTRAINT "FK_51adb49e7f8df35dd23e01c4830" FOREIGN KEY ("applicationId") REFERENCES "core"."application"("id") ON DELETE CASCADE ON UPDATE NO ACTION',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."applicationVariable" ALTER COLUMN "universalIdentifier" SET NOT NULL',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'CREATE UNIQUE INDEX "IDX_44ecebdf70cbed17f89527b36b" ON "core"."applicationVariable" ("workspaceId", "universalIdentifier") ',
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'DROP INDEX "core"."IDX_44ecebdf70cbed17f89527b36b"',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."applicationVariable" ALTER COLUMN "universalIdentifier" DROP NOT NULL',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."applicationVariable" DROP CONSTRAINT "FK_51adb49e7f8df35dd23e01c4830"',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."applicationVariable" ALTER COLUMN "applicationId" DROP NOT NULL',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE "core"."applicationVariable" ADD CONSTRAINT "FK_51adb49e7f8df35dd23e01c4830" FOREIGN KEY ("applicationId") REFERENCES "core"."application"("id") ON DELETE CASCADE ON UPDATE NO ACTION',
|
||||
);
|
||||
}
|
||||
}
|
||||
-4
@@ -26,8 +26,6 @@ import { AddToolAndWorkflowActionTriggerSettingsFastInstanceCommand } from 'src/
|
||||
import { MigrateToolTriggerSettingsSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-instance-command-slow-1797000002000-migrate-tool-trigger-settings';
|
||||
import { ConnectionProviderSyncableEntityFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-instance-command-fast-1777896012579-connection-provider-syncable-entity';
|
||||
import { RemoveUserDefaultAvatarUrlFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-instance-command-fast-1777915958318-remove-user-default-avatar-url';
|
||||
import { TransformApplicationVariableToSyncableEntityFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-instance-command-fast-1777966965587-transform-application-variable-to-syncable-entity';
|
||||
import { BackfillApplicationVariableUniversalIdentifierSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-instance-command-slow-1777966965588-backfill-application-variable-universal-identifier';
|
||||
|
||||
export const INSTANCE_COMMANDS = [
|
||||
AddViewFieldGroupIdIndexOnViewFieldFastInstanceCommand,
|
||||
@@ -56,6 +54,4 @@ export const INSTANCE_COMMANDS = [
|
||||
MigrateToolTriggerSettingsSlowInstanceCommand,
|
||||
ConnectionProviderSyncableEntityFastInstanceCommand,
|
||||
RemoveUserDefaultAvatarUrlFastInstanceCommand,
|
||||
TransformApplicationVariableToSyncableEntityFastInstanceCommand,
|
||||
BackfillApplicationVariableUniversalIdentifierSlowInstanceCommand,
|
||||
];
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined, isPlainObject } from 'twenty-shared/utils';
|
||||
|
||||
import { type CompositeFieldGroupByDefinition } from 'src/engine/api/common/common-args-processors/group-by-arg-processor/types/composite-field-group-by-definition.type';
|
||||
import { isGroupByDateFieldDefinition } from 'src/engine/api/common/common-args-processors/group-by-arg-processor/utils/is-group-by-date-field-definition.util';
|
||||
import { isRelationNestedFieldSupportedInGroupBy } from 'src/engine/api/common/common-args-processors/group-by-arg-processor/utils/is-relation-nested-field-supported-in-group-by.util';
|
||||
import { validateSingleKeyForGroupByOrThrow } from 'src/engine/api/common/common-args-processors/group-by-arg-processor/utils/validate-single-key-for-group-by-or-throw.util';
|
||||
|
||||
@@ -11,6 +11,10 @@ import { CreateApiKeyInput } from 'src/engine/core-modules/api-key/dtos/create-a
|
||||
import { GetApiKeyInput } from 'src/engine/core-modules/api-key/dtos/get-api-key.input';
|
||||
import { RevokeApiKeyInput } from 'src/engine/core-modules/api-key/dtos/revoke-api-key.input';
|
||||
import { UpdateApiKeyInput } from 'src/engine/core-modules/api-key/dtos/update-api-key.input';
|
||||
import {
|
||||
ApiKeyException,
|
||||
ApiKeyExceptionCode,
|
||||
} from 'src/engine/core-modules/api-key/exceptions/api-key.exception';
|
||||
import { apiKeyGraphqlApiExceptionHandler } from 'src/engine/core-modules/api-key/utils/api-key-graphql-api-exception-handler.util';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
|
||||
+10
@@ -16,6 +16,7 @@ import { ApplicationService } from 'src/engine/core-modules/application/applicat
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { buildFromToAllUniversalFlatEntityMaps } from 'src/engine/core-modules/application/application-manifest/utils/build-from-to-all-universal-flat-entity-maps.util';
|
||||
import { getApplicationSubAllFlatEntityMaps } from 'src/engine/core-modules/application/application-manifest/utils/get-application-sub-all-flat-entity-maps.util';
|
||||
import { ApplicationVariableEntityService } from 'src/engine/core-modules/application/application-variable/application-variable.service';
|
||||
import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service';
|
||||
import { createEmptyAllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-all-flat-entity-maps.constant';
|
||||
import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util';
|
||||
@@ -31,6 +32,7 @@ export class ApplicationSyncService {
|
||||
|
||||
constructor(
|
||||
private readonly applicationService: ApplicationService,
|
||||
private readonly applicationVariableService: ApplicationVariableEntityService,
|
||||
private readonly applicationManifestMigrationService: ApplicationManifestMigrationService,
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
private readonly workspaceCacheService: WorkspaceCacheService,
|
||||
@@ -143,6 +145,14 @@ export class ApplicationSyncService {
|
||||
);
|
||||
}
|
||||
|
||||
await this.applicationVariableService.upsertManyApplicationVariableEntities(
|
||||
{
|
||||
applicationVariables: manifest.application.applicationVariables,
|
||||
applicationId: application.id,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
const resolvedRegistrationId =
|
||||
applicationRegistrationId ?? application.applicationRegistrationId;
|
||||
|
||||
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
import { type UniversalFlatApplicationVariable } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-application-variable.type';
|
||||
|
||||
export const fromApplicationVariableManifestToUniversalFlatApplicationVariable =
|
||||
({
|
||||
key,
|
||||
universalIdentifier,
|
||||
description,
|
||||
value,
|
||||
isSecret,
|
||||
applicationUniversalIdentifier,
|
||||
now,
|
||||
}: {
|
||||
key: string;
|
||||
universalIdentifier: string;
|
||||
description?: string;
|
||||
value?: string;
|
||||
isSecret?: boolean;
|
||||
applicationUniversalIdentifier: string;
|
||||
now: string;
|
||||
}): UniversalFlatApplicationVariable => {
|
||||
return {
|
||||
universalIdentifier,
|
||||
applicationUniversalIdentifier,
|
||||
key,
|
||||
value: isSecret ? '' : (value ?? ''), // We protect secret variable by not syncing its value at all
|
||||
description: description ?? '',
|
||||
isSecret: isSecret ?? false,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
};
|
||||
-23
@@ -4,7 +4,6 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { generateIndexForFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/generate-index-for-flat-field-metadata.util';
|
||||
|
||||
import { fromApplicationVariableManifestToUniversalFlatApplicationVariable } from 'src/engine/core-modules/application/application-manifest/converters/from-application-variable-manifest-to-universal-flat-application-variable.util';
|
||||
import { fromCommandMenuItemManifestToUniversalFlatCommandMenuItem } from 'src/engine/core-modules/application/application-manifest/converters/from-command-menu-item-manifest-to-universal-flat-command-menu-item.util';
|
||||
import { fromConnectionProviderManifestToUniversalFlatConnectionProvider } from 'src/engine/core-modules/application/application-manifest/converters/from-connection-provider-manifest-to-universal-flat-connection-provider.util';
|
||||
import { fromFieldManifestToUniversalFlatFieldMetadata } from 'src/engine/core-modules/application/application-manifest/converters/from-field-manifest-to-universal-flat-field-metadata.util';
|
||||
@@ -432,28 +431,6 @@ export const computeApplicationManifestAllUniversalFlatEntityMaps = ({
|
||||
}
|
||||
}
|
||||
|
||||
for (const [key, applicationVariableManifest] of Object.entries(
|
||||
manifest.application.applicationVariables ?? {},
|
||||
)) {
|
||||
addUniversalFlatEntityToUniversalFlatEntityMapsThroughMutationOrThrow({
|
||||
universalFlatEntity:
|
||||
fromApplicationVariableManifestToUniversalFlatApplicationVariable({
|
||||
key,
|
||||
universalIdentifier: applicationVariableManifest.universalIdentifier,
|
||||
value:
|
||||
'value' in applicationVariableManifest
|
||||
? applicationVariableManifest.value
|
||||
: undefined,
|
||||
description: applicationVariableManifest.description,
|
||||
isSecret: applicationVariableManifest.isSecret,
|
||||
applicationUniversalIdentifier,
|
||||
now,
|
||||
}),
|
||||
universalFlatEntityMapsToMutate:
|
||||
allUniversalFlatEntityMaps.flatApplicationVariableMaps,
|
||||
});
|
||||
}
|
||||
|
||||
for (const commandMenuItemManifest of manifest.commandMenuItems ?? []) {
|
||||
if (!isDefined(commandMenuItemManifest.frontComponentUniversalIdentifier)) {
|
||||
throw new Error(
|
||||
|
||||
+1
@@ -32,6 +32,7 @@ import { RotateClientSecretDTO } from 'src/engine/core-modules/application/appli
|
||||
import { TransferApplicationRegistrationOwnershipInput } from 'src/engine/core-modules/application/application-registration/dtos/transfer-application-registration-ownership.input';
|
||||
import { UpdateApplicationRegistrationInput } from 'src/engine/core-modules/application/application-registration/dtos/update-application-registration.input';
|
||||
import { ApplicationRegistrationSourceType } from 'src/engine/core-modules/application/application-registration/enums/application-registration-source-type.enum';
|
||||
import { DomainServerConfigService } from 'src/engine/core-modules/domain/domain-server-config/services/domain-server-config.service';
|
||||
import { AuthGraphqlApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter';
|
||||
import { FileUrlService } from 'src/engine/core-modules/file/file-url/file-url.service';
|
||||
import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter';
|
||||
|
||||
+214
@@ -159,6 +159,220 @@ describe('ApplicationVariableEntityService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('upsertManyApplicationVariableEntities', () => {
|
||||
it('should encrypt secret values when creating new variables', async () => {
|
||||
repository.find.mockResolvedValue([]);
|
||||
repository.save.mockResolvedValue({} as any);
|
||||
repository.delete.mockResolvedValue({ affected: 0 } as any);
|
||||
|
||||
await service.upsertManyApplicationVariableEntities({
|
||||
applicationVariables: {
|
||||
SECRET_KEY: {
|
||||
universalIdentifier: 'secret-key-123',
|
||||
value: 'my-secret',
|
||||
description: 'A secret key',
|
||||
isSecret: true,
|
||||
},
|
||||
},
|
||||
applicationId: mockApplicationId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(secretEncryptionService.encrypt).toHaveBeenCalledWith('my-secret');
|
||||
expect(repository.save).toHaveBeenCalledWith([
|
||||
{
|
||||
key: 'SECRET_KEY',
|
||||
value: 'encrypted_my-secret',
|
||||
description: 'A secret key',
|
||||
isSecret: true,
|
||||
applicationId: mockApplicationId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should not encrypt non-secret values when creating new variables', async () => {
|
||||
repository.find.mockResolvedValue([]);
|
||||
repository.save.mockResolvedValue({} as any);
|
||||
repository.delete.mockResolvedValue({ affected: 0 } as any);
|
||||
|
||||
await service.upsertManyApplicationVariableEntities({
|
||||
applicationVariables: {
|
||||
PUBLIC_URL: {
|
||||
universalIdentifier: 'public-url-123',
|
||||
value: 'https://example.com',
|
||||
description: 'Public URL',
|
||||
isSecret: false,
|
||||
},
|
||||
},
|
||||
applicationId: mockApplicationId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(secretEncryptionService.encrypt).not.toHaveBeenCalled();
|
||||
expect(repository.save).toHaveBeenCalledWith([
|
||||
{
|
||||
key: 'PUBLIC_URL',
|
||||
value: 'https://example.com',
|
||||
description: 'Public URL',
|
||||
isSecret: false,
|
||||
applicationId: mockApplicationId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle undefined isSecret as false', async () => {
|
||||
repository.find.mockResolvedValue([]);
|
||||
repository.save.mockResolvedValue({} as any);
|
||||
repository.delete.mockResolvedValue({ affected: 0 } as any);
|
||||
|
||||
await service.upsertManyApplicationVariableEntities({
|
||||
applicationVariables: {
|
||||
SOME_VAR: {
|
||||
universalIdentifier: 'some-var-123',
|
||||
value: 'some-value',
|
||||
description: 'Some variable',
|
||||
},
|
||||
},
|
||||
applicationId: mockApplicationId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(secretEncryptionService.encrypt).not.toHaveBeenCalled();
|
||||
expect(repository.save).toHaveBeenCalledWith([
|
||||
expect.objectContaining({
|
||||
isSecret: false,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should update existing variables without changing values', async () => {
|
||||
const existingVariable = {
|
||||
id: '1',
|
||||
key: 'EXISTING_VAR',
|
||||
value: 'existing-encrypted-value',
|
||||
isSecret: true,
|
||||
applicationId: mockApplicationId,
|
||||
} as ApplicationVariableEntity;
|
||||
|
||||
repository.find.mockResolvedValue([existingVariable]);
|
||||
repository.save.mockResolvedValue({} as any);
|
||||
repository.delete.mockResolvedValue({ affected: 0 } as any);
|
||||
|
||||
await service.upsertManyApplicationVariableEntities({
|
||||
applicationVariables: {
|
||||
EXISTING_VAR: {
|
||||
universalIdentifier: 'existing-var-123',
|
||||
value: 'new-value',
|
||||
description: 'Updated description',
|
||||
isSecret: true,
|
||||
},
|
||||
},
|
||||
applicationId: mockApplicationId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(repository.save).toHaveBeenCalledWith([
|
||||
{
|
||||
id: '1',
|
||||
description: 'Updated description',
|
||||
isSecret: true,
|
||||
},
|
||||
]);
|
||||
expect(repository.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should update existing value when shouldUpdateValue is true', async () => {
|
||||
const existingVariable = {
|
||||
id: '1',
|
||||
key: 'EXISTING_VAR',
|
||||
value: 'existing-encrypted-value',
|
||||
isSecret: true,
|
||||
applicationId: mockApplicationId,
|
||||
} as ApplicationVariableEntity;
|
||||
|
||||
repository.find.mockResolvedValue([existingVariable]);
|
||||
repository.save.mockResolvedValue({} as any);
|
||||
repository.delete.mockResolvedValue({ affected: 0 } as any);
|
||||
|
||||
await service.upsertManyApplicationVariableEntities({
|
||||
applicationVariables: {
|
||||
EXISTING_VAR: {
|
||||
universalIdentifier: 'existing-var-123',
|
||||
value: 'new-value',
|
||||
description: 'Updated description',
|
||||
isSecret: true,
|
||||
},
|
||||
},
|
||||
applicationId: mockApplicationId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
shouldUpdateValue: true,
|
||||
});
|
||||
|
||||
expect(repository.save).toHaveBeenCalledWith([
|
||||
{
|
||||
id: '1',
|
||||
description: 'Updated description',
|
||||
value: 'encrypted_new-value',
|
||||
isSecret: true,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should update existing value if isSecret changes', async () => {
|
||||
const existingVariable = {
|
||||
id: '1',
|
||||
key: 'EXISTING_VAR',
|
||||
value: 'existing-encrypted-value',
|
||||
isSecret: true,
|
||||
applicationId: mockApplicationId,
|
||||
} as ApplicationVariableEntity;
|
||||
|
||||
repository.find.mockResolvedValue([existingVariable]);
|
||||
repository.save.mockResolvedValue({} as any);
|
||||
repository.delete.mockResolvedValue({ affected: 0 } as any);
|
||||
|
||||
await service.upsertManyApplicationVariableEntities({
|
||||
applicationVariables: {
|
||||
EXISTING_VAR: {
|
||||
universalIdentifier: 'existing-var-123',
|
||||
value: 'new-value',
|
||||
description: 'Updated description',
|
||||
isSecret: false,
|
||||
},
|
||||
},
|
||||
applicationId: mockApplicationId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(repository.save).toHaveBeenCalledWith([
|
||||
{
|
||||
id: '1',
|
||||
description: 'Updated description',
|
||||
value: 'new-value',
|
||||
isSecret: false,
|
||||
},
|
||||
]);
|
||||
expect(repository.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle undefined applicationVariables', async () => {
|
||||
await service.upsertManyApplicationVariableEntities({
|
||||
applicationVariables: undefined,
|
||||
applicationId: mockApplicationId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(repository.find).not.toHaveBeenCalled();
|
||||
expect(repository.save).not.toHaveBeenCalled();
|
||||
expect(repository.update).not.toHaveBeenCalled();
|
||||
expect(
|
||||
workspaceCacheService.invalidateAndRecompute,
|
||||
).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDisplayValue', () => {
|
||||
it('should return plain value for non-secret variables', () => {
|
||||
const variable = {
|
||||
|
||||
+1
-6
@@ -6,10 +6,7 @@ import {
|
||||
ApplicationVariableEntityException,
|
||||
ApplicationVariableEntityExceptionCode,
|
||||
} from 'src/engine/core-modules/application/application-variable/application-variable.exception';
|
||||
import {
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { NotFoundError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
|
||||
@Catch(ApplicationVariableEntityException)
|
||||
export class ApplicationVariableEntityExceptionFilter
|
||||
@@ -19,8 +16,6 @@ export class ApplicationVariableEntityExceptionFilter
|
||||
switch (exception.code) {
|
||||
case ApplicationVariableEntityExceptionCode.APPLICATION_VARIABLE_NOT_FOUND:
|
||||
throw new NotFoundError(exception);
|
||||
case ApplicationVariableEntityExceptionCode.INVALID_APPLICATION_VARIABLE_INPUT:
|
||||
throw new UserInputError(exception);
|
||||
default:
|
||||
assertUnreachable(exception.code);
|
||||
}
|
||||
|
||||
+34
-2
@@ -5,23 +5,41 @@ import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
Unique,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import type { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { EntityRelation } from 'src/engine/workspace-manager/workspace-migration/types/entity-relation.interface';
|
||||
|
||||
@Entity({
|
||||
name: 'applicationVariable',
|
||||
schema: 'core',
|
||||
})
|
||||
@ObjectType('ApplicationVariable')
|
||||
export class ApplicationVariableEntity extends SyncableEntity {
|
||||
@Unique('IDX_APPLICATION_VARIABLE_KEY_APPLICATION_ID_UNIQUE', [
|
||||
'key',
|
||||
'applicationId',
|
||||
])
|
||||
export class ApplicationVariableEntity {
|
||||
@IDField(() => UUIDScalarType)
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
@Index()
|
||||
workspaceId: string;
|
||||
|
||||
@ManyToOne('WorkspaceEntity', { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'workspaceId' })
|
||||
workspace: EntityRelation<WorkspaceEntity>;
|
||||
|
||||
@Column({ nullable: false, type: 'text' })
|
||||
key: string;
|
||||
|
||||
@@ -34,6 +52,20 @@ export class ApplicationVariableEntity extends SyncableEntity {
|
||||
@Column({ nullable: false, type: 'boolean', default: false })
|
||||
isSecret: boolean;
|
||||
|
||||
@Column({ nullable: true, type: 'uuid' })
|
||||
applicationId?: string;
|
||||
|
||||
@ManyToOne(
|
||||
() => ApplicationEntity,
|
||||
(application) => application.applicationVariables,
|
||||
{
|
||||
onDelete: 'CASCADE',
|
||||
nullable: true,
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: 'applicationId' })
|
||||
application: EntityRelation<ApplicationEntity> | null;
|
||||
|
||||
@CreateDateColumn({ type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
|
||||
-3
@@ -6,7 +6,6 @@ import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export enum ApplicationVariableEntityExceptionCode {
|
||||
APPLICATION_VARIABLE_NOT_FOUND = 'APPLICATION_VARIABLE_NOT_FOUND',
|
||||
INVALID_APPLICATION_VARIABLE_INPUT = 'INVALID_APPLICATION_VARIABLE_INPUT',
|
||||
}
|
||||
|
||||
const getApplicationVariableEntityExceptionUserFriendlyMessage = (
|
||||
@@ -15,8 +14,6 @@ const getApplicationVariableEntityExceptionUserFriendlyMessage = (
|
||||
switch (code) {
|
||||
case ApplicationVariableEntityExceptionCode.APPLICATION_VARIABLE_NOT_FOUND:
|
||||
return msg`Application variable not found.`;
|
||||
case ApplicationVariableEntityExceptionCode.INVALID_APPLICATION_VARIABLE_INPUT:
|
||||
return msg`Invalid application variable input.`;
|
||||
default:
|
||||
assertUnreachable(code);
|
||||
}
|
||||
|
||||
+1
-4
@@ -3,24 +3,21 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
||||
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { ApplicationVariableEntity } from 'src/engine/core-modules/application/application-variable/application-variable.entity';
|
||||
import { ApplicationVariableEntityResolver } from 'src/engine/core-modules/application/application-variable/application-variable.resolver';
|
||||
import { ApplicationVariableEntityService } from 'src/engine/core-modules/application/application-variable/application-variable.service';
|
||||
import { WorkspaceApplicationVariableMapCacheService } from 'src/engine/core-modules/application/application-variable/workspace-application-variable-map-cache.service';
|
||||
import { SecretEncryptionModule } from 'src/engine/core-modules/secret-encryption/secret-encryption.module';
|
||||
import { FlatApplicationVariableModule } from 'src/engine/metadata-modules/flat-application-variable/flat-application-variable.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
NestjsQueryTypeOrmModule.forFeature([ApplicationVariableEntity]),
|
||||
TypeOrmModule.forFeature([ApplicationVariableEntity, ApplicationEntity]),
|
||||
TypeOrmModule.forFeature([ApplicationVariableEntity]),
|
||||
PermissionsModule,
|
||||
WorkspaceCacheModule,
|
||||
SecretEncryptionModule,
|
||||
FlatApplicationVariableModule,
|
||||
],
|
||||
providers: [
|
||||
ApplicationVariableEntityService,
|
||||
|
||||
+131
-4
@@ -1,8 +1,9 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { ApplicationVariables } from 'twenty-shared/application';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
import { In, Not, Repository } from 'typeorm';
|
||||
|
||||
import { ApplicationVariableEntity } from 'src/engine/core-modules/application/application-variable/application-variable.entity';
|
||||
import {
|
||||
@@ -22,6 +23,14 @@ export class ApplicationVariableEntityService {
|
||||
private readonly secretEncryptionService: SecretEncryptionService,
|
||||
) {}
|
||||
|
||||
private encryptSecretValue(value: string, isSecret: boolean): string {
|
||||
if (!isSecret) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.secretEncryptionService.encrypt(value);
|
||||
}
|
||||
|
||||
getDisplayValue(applicationVariable: ApplicationVariableEntity): string {
|
||||
if (!applicationVariable.isSecret) {
|
||||
return applicationVariable.value;
|
||||
@@ -33,6 +42,48 @@ export class ApplicationVariableEntityService {
|
||||
});
|
||||
}
|
||||
|
||||
// Decrypted plaintext value. Server-side only — never expose via GraphQL.
|
||||
// Used by trusted server flows that need the raw secret (e.g. exchanging an
|
||||
// OAuth client secret with a third-party provider).
|
||||
getRawValue(applicationVariable: ApplicationVariableEntity): string {
|
||||
if (!applicationVariable.isSecret) {
|
||||
return applicationVariable.value;
|
||||
}
|
||||
|
||||
return this.secretEncryptionService.decrypt(applicationVariable.value);
|
||||
}
|
||||
|
||||
async findOneByKey({
|
||||
applicationId,
|
||||
key,
|
||||
}: {
|
||||
applicationId: string;
|
||||
key: string;
|
||||
}): Promise<ApplicationVariableEntity | null> {
|
||||
return this.applicationVariableRepository.findOne({
|
||||
where: { applicationId, key },
|
||||
});
|
||||
}
|
||||
|
||||
async getRawValueByKeyOrThrow({
|
||||
applicationId,
|
||||
key,
|
||||
}: {
|
||||
applicationId: string;
|
||||
key: string;
|
||||
}): Promise<string> {
|
||||
const variable = await this.findOneByKey({ applicationId, key });
|
||||
|
||||
if (!isDefined(variable)) {
|
||||
throw new ApplicationVariableEntityException(
|
||||
`Application variable "${key}" not found for application ${applicationId}`,
|
||||
ApplicationVariableEntityExceptionCode.APPLICATION_VARIABLE_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return this.getRawValue(variable);
|
||||
}
|
||||
|
||||
async update({
|
||||
key,
|
||||
plainTextValue,
|
||||
@@ -54,9 +105,10 @@ export class ApplicationVariableEntityService {
|
||||
);
|
||||
}
|
||||
|
||||
const encryptedValue = existingVariable.isSecret
|
||||
? this.secretEncryptionService.encrypt(plainTextValue)
|
||||
: plainTextValue;
|
||||
const encryptedValue = this.encryptSecretValue(
|
||||
plainTextValue,
|
||||
existingVariable.isSecret,
|
||||
);
|
||||
|
||||
await this.applicationVariableRepository.update(
|
||||
{ key, applicationId },
|
||||
@@ -69,4 +121,79 @@ export class ApplicationVariableEntityService {
|
||||
'applicationVariableMaps',
|
||||
]);
|
||||
}
|
||||
|
||||
async upsertManyApplicationVariableEntities({
|
||||
applicationVariables,
|
||||
applicationId,
|
||||
workspaceId,
|
||||
shouldUpdateValue = false,
|
||||
}: {
|
||||
applicationVariables?: ApplicationVariables;
|
||||
applicationId: string;
|
||||
workspaceId: string;
|
||||
shouldUpdateValue?: boolean;
|
||||
}) {
|
||||
if (!isDefined(applicationVariables)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const keys = Object.keys(applicationVariables);
|
||||
|
||||
const existingVariables = await this.applicationVariableRepository.find({
|
||||
where: {
|
||||
applicationId,
|
||||
key: In(keys),
|
||||
},
|
||||
});
|
||||
|
||||
const existingVariablesByKey = new Map(
|
||||
existingVariables.map((variable) => [variable.key, variable]),
|
||||
);
|
||||
|
||||
const entitiesToSave: Partial<ApplicationVariableEntity>[] = [];
|
||||
|
||||
for (const [key, { value, description, isSecret }] of Object.entries(
|
||||
applicationVariables,
|
||||
)) {
|
||||
const existingVariable = existingVariablesByKey.get(key);
|
||||
const isSecretValue = isSecret ?? false;
|
||||
const encryptedValue = this.encryptSecretValue(
|
||||
value ?? '',
|
||||
isSecretValue,
|
||||
);
|
||||
|
||||
if (isDefined(existingVariable)) {
|
||||
entitiesToSave.push({
|
||||
id: existingVariable.id,
|
||||
description: description ?? '',
|
||||
isSecret: isSecretValue,
|
||||
...(shouldUpdateValue || existingVariable.isSecret !== isSecretValue
|
||||
? { value: encryptedValue }
|
||||
: {}),
|
||||
});
|
||||
} else {
|
||||
entitiesToSave.push({
|
||||
key,
|
||||
value: encryptedValue,
|
||||
description: description ?? '',
|
||||
isSecret: isSecretValue,
|
||||
applicationId,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (entitiesToSave.length > 0) {
|
||||
await this.applicationVariableRepository.save(entitiesToSave);
|
||||
}
|
||||
|
||||
await this.applicationVariableRepository.delete({
|
||||
applicationId,
|
||||
key: Not(In(keys)),
|
||||
});
|
||||
|
||||
await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [
|
||||
'applicationVariableMaps',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -1,5 +1,6 @@
|
||||
import { type FlatApplicationVariable } from 'src/engine/metadata-modules/flat-application-variable/types/flat-application-variable.type';
|
||||
import type { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatApplicationVariable } from 'src/engine/core-modules/application/application-variable/types/flat-application-variable.type';
|
||||
|
||||
export type ApplicationVariableCacheMaps =
|
||||
FlatEntityMaps<FlatApplicationVariable>;
|
||||
export type ApplicationVariableCacheMaps = {
|
||||
byId: Partial<Record<string, FlatApplicationVariable>>;
|
||||
byApplicationId: Partial<Record<string, FlatApplicationVariable[]>>;
|
||||
};
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
|
||||
import { type ApplicationVariableEntity } from 'src/engine/core-modules/application/application-variable/application-variable.entity';
|
||||
import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
|
||||
|
||||
export type FlatApplicationVariable = FlatEntityFrom<ApplicationVariableEntity>;
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { type ApplicationVariableEntity } from 'src/engine/core-modules/application/application-variable/application-variable.entity';
|
||||
import { type FlatApplicationVariable } from 'src/engine/core-modules/application/application-variable/types/flat-application-variable.type';
|
||||
|
||||
export const fromApplicationVariableEntityToFlatApplicationVariable = (
|
||||
entity: ApplicationVariableEntity,
|
||||
): FlatApplicationVariable => ({
|
||||
id: entity.id,
|
||||
key: entity.key,
|
||||
value: entity.value,
|
||||
description: entity.description,
|
||||
isSecret: entity.isSecret,
|
||||
applicationId: entity.applicationId,
|
||||
workspaceId: entity.workspaceId,
|
||||
createdAt: entity.createdAt.toISOString(),
|
||||
updatedAt: entity.updatedAt.toISOString(),
|
||||
});
|
||||
+34
-28
@@ -2,17 +2,14 @@ import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkspaceCacheProvider } from 'src/engine/workspace-cache/interfaces/workspace-cache-provider.service';
|
||||
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { ApplicationVariableEntity } from 'src/engine/core-modules/application/application-variable/application-variable.entity';
|
||||
import { type ApplicationVariableCacheMaps } from 'src/engine/core-modules/application/application-variable/types/application-variable-cache-maps.type';
|
||||
import { fromApplicationVariableEntityToFlatApplicationVariable } from 'src/engine/metadata-modules/flat-application-variable/utils/from-application-variable-entity-to-flat-application-variable.util';
|
||||
import { fromApplicationVariableEntityToFlatApplicationVariable } from 'src/engine/core-modules/application/application-variable/utils/from-application-variable-entity-to-flat-application-variable.util';
|
||||
import { WorkspaceCache } from 'src/engine/workspace-cache/decorators/workspace-cache.decorator';
|
||||
import { createIdToUniversalIdentifierMap } from 'src/engine/workspace-cache/utils/create-id-to-universal-identifier-map.util';
|
||||
import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-flat-entity-maps.constant';
|
||||
import { addFlatEntityToFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/utils/add-flat-entity-to-flat-entity-maps-through-mutation-or-throw.util';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceCache('applicationVariableMaps')
|
||||
@@ -20,8 +17,6 @@ export class WorkspaceApplicationVariableMapCacheService extends WorkspaceCacheP
|
||||
constructor(
|
||||
@InjectRepository(ApplicationVariableEntity)
|
||||
private readonly applicationVariableRepository: Repository<ApplicationVariableEntity>,
|
||||
@InjectRepository(ApplicationEntity)
|
||||
private readonly applicationRepository: Repository<ApplicationEntity>,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -29,32 +24,43 @@ export class WorkspaceApplicationVariableMapCacheService extends WorkspaceCacheP
|
||||
async computeForCache(
|
||||
workspaceId: string,
|
||||
): Promise<ApplicationVariableCacheMaps> {
|
||||
const [applicationVariableEntities, applications] = await Promise.all([
|
||||
this.applicationVariableRepository.find({
|
||||
where: { workspaceId },
|
||||
}),
|
||||
this.applicationRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'universalIdentifier'],
|
||||
}),
|
||||
]);
|
||||
const applicationVariableEntities = await this.applicationVariableRepository
|
||||
.createQueryBuilder('applicationVariable')
|
||||
.innerJoin('applicationVariable.application', 'application')
|
||||
.where('application.workspaceId = :workspaceId', { workspaceId })
|
||||
.getMany();
|
||||
|
||||
const applicationIdToUniversalIdentifierMap =
|
||||
createIdToUniversalIdentifierMap(applications);
|
||||
|
||||
const applicationVariableMaps = createEmptyFlatEntityMaps();
|
||||
const applicationVariableMaps: ApplicationVariableCacheMaps = {
|
||||
byId: {},
|
||||
byApplicationId: {},
|
||||
};
|
||||
|
||||
for (const entity of applicationVariableEntities) {
|
||||
const flatApplicationVariable =
|
||||
fromApplicationVariableEntityToFlatApplicationVariable({
|
||||
entity,
|
||||
applicationIdToUniversalIdentifierMap,
|
||||
});
|
||||
fromApplicationVariableEntityToFlatApplicationVariable(entity);
|
||||
|
||||
addFlatEntityToFlatEntityMapsThroughMutationOrThrow({
|
||||
flatEntity: flatApplicationVariable,
|
||||
flatEntityMapsToMutate: applicationVariableMaps,
|
||||
});
|
||||
applicationVariableMaps.byId[flatApplicationVariable.id] =
|
||||
flatApplicationVariable;
|
||||
|
||||
if (!isDefined(flatApplicationVariable.applicationId)) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
!isDefined(
|
||||
applicationVariableMaps.byApplicationId[
|
||||
flatApplicationVariable.applicationId
|
||||
],
|
||||
)
|
||||
) {
|
||||
applicationVariableMaps.byApplicationId[
|
||||
flatApplicationVariable.applicationId
|
||||
] = [flatApplicationVariable];
|
||||
continue;
|
||||
}
|
||||
|
||||
applicationVariableMaps.byApplicationId[
|
||||
flatApplicationVariable.applicationId
|
||||
]?.push(flatApplicationVariable);
|
||||
}
|
||||
|
||||
return applicationVariableMaps;
|
||||
|
||||
@@ -588,26 +588,6 @@ msgstr "Toepassingsregistrasieveranderlike nie gevind nie."
|
||||
msgid "Application upgrade failed."
|
||||
msgstr "Toepassingsopgradering het misluk."
|
||||
|
||||
#. js-lingui-id: 5MJQ4s
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
msgid "Application variable key is required"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: K+YEBR
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
msgid "Application variable key must be unique"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: e6tjp1
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
msgid "Application variable not found"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: 9qtW5o
|
||||
#: src/engine/core-modules/application/application-variable/application-variable.exception.ts
|
||||
msgid "Application variable not found."
|
||||
@@ -3207,11 +3187,6 @@ msgstr "Ongeldige agent-invoer."
|
||||
msgid "Invalid aggregate fields parameter."
|
||||
msgstr "Ongeldige aggregaatvelde-parameter."
|
||||
|
||||
#. js-lingui-id: 56bSD0
|
||||
#: src/engine/core-modules/application/application-variable/application-variable.exception.ts
|
||||
msgid "Invalid application variable input."
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: KNrRpL
|
||||
#: src/engine/metadata-modules/permissions/permissions.exception.ts
|
||||
msgid "Invalid argument provided."
|
||||
|
||||
@@ -588,26 +588,6 @@ msgstr "متغير تسجيل التطبيق غير موجود."
|
||||
msgid "Application upgrade failed."
|
||||
msgstr "فشلت ترقية التطبيق."
|
||||
|
||||
#. js-lingui-id: 5MJQ4s
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
msgid "Application variable key is required"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: K+YEBR
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
msgid "Application variable key must be unique"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: e6tjp1
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
msgid "Application variable not found"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: 9qtW5o
|
||||
#: src/engine/core-modules/application/application-variable/application-variable.exception.ts
|
||||
msgid "Application variable not found."
|
||||
@@ -3207,11 +3187,6 @@ msgstr "إدخال الوكيل غير صالح."
|
||||
msgid "Invalid aggregate fields parameter."
|
||||
msgstr "معامل حقول التجميع غير صالح."
|
||||
|
||||
#. js-lingui-id: 56bSD0
|
||||
#: src/engine/core-modules/application/application-variable/application-variable.exception.ts
|
||||
msgid "Invalid application variable input."
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: KNrRpL
|
||||
#: src/engine/metadata-modules/permissions/permissions.exception.ts
|
||||
msgid "Invalid argument provided."
|
||||
|
||||
@@ -588,26 +588,6 @@ msgstr "Variable de registre d'aplicació no trobada."
|
||||
msgid "Application upgrade failed."
|
||||
msgstr "L'actualització de l'aplicació ha fallat."
|
||||
|
||||
#. js-lingui-id: 5MJQ4s
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
msgid "Application variable key is required"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: K+YEBR
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
msgid "Application variable key must be unique"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: e6tjp1
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
msgid "Application variable not found"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: 9qtW5o
|
||||
#: src/engine/core-modules/application/application-variable/application-variable.exception.ts
|
||||
msgid "Application variable not found."
|
||||
@@ -3207,11 +3187,6 @@ msgstr "Entrada d'agent no vàlida."
|
||||
msgid "Invalid aggregate fields parameter."
|
||||
msgstr "Paràmetre de camps agregats no vàlid."
|
||||
|
||||
#. js-lingui-id: 56bSD0
|
||||
#: src/engine/core-modules/application/application-variable/application-variable.exception.ts
|
||||
msgid "Invalid application variable input."
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: KNrRpL
|
||||
#: src/engine/metadata-modules/permissions/permissions.exception.ts
|
||||
msgid "Invalid argument provided."
|
||||
|
||||
@@ -588,26 +588,6 @@ msgstr "Proměnná registrace aplikace nenalezena."
|
||||
msgid "Application upgrade failed."
|
||||
msgstr "Aktualizace aplikace se nezdařila."
|
||||
|
||||
#. js-lingui-id: 5MJQ4s
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
msgid "Application variable key is required"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: K+YEBR
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
msgid "Application variable key must be unique"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: e6tjp1
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
msgid "Application variable not found"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: 9qtW5o
|
||||
#: src/engine/core-modules/application/application-variable/application-variable.exception.ts
|
||||
msgid "Application variable not found."
|
||||
@@ -3207,11 +3187,6 @@ msgstr "Neplatný vstup agenta."
|
||||
msgid "Invalid aggregate fields parameter."
|
||||
msgstr "Neplatný parametr agregačních polí."
|
||||
|
||||
#. js-lingui-id: 56bSD0
|
||||
#: src/engine/core-modules/application/application-variable/application-variable.exception.ts
|
||||
msgid "Invalid application variable input."
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: KNrRpL
|
||||
#: src/engine/metadata-modules/permissions/permissions.exception.ts
|
||||
msgid "Invalid argument provided."
|
||||
|
||||
@@ -588,26 +588,6 @@ msgstr "Applikationsregistreringsvariabel ikke fundet."
|
||||
msgid "Application upgrade failed."
|
||||
msgstr "Applikationsopgradering mislykkedes."
|
||||
|
||||
#. js-lingui-id: 5MJQ4s
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
msgid "Application variable key is required"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: K+YEBR
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
msgid "Application variable key must be unique"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: e6tjp1
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
#: src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-application-variable-validator.service.ts
|
||||
msgid "Application variable not found"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: 9qtW5o
|
||||
#: src/engine/core-modules/application/application-variable/application-variable.exception.ts
|
||||
msgid "Application variable not found."
|
||||
@@ -3207,11 +3187,6 @@ msgstr "Ugyldigt agentinput."
|
||||
msgid "Invalid aggregate fields parameter."
|
||||
msgstr "Ugyldig parameter for aggregerede felter."
|
||||
|
||||
#. js-lingui-id: 56bSD0
|
||||
#: src/engine/core-modules/application/application-variable/application-variable.exception.ts
|
||||
msgid "Invalid application variable input."
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: KNrRpL
|
||||
#: src/engine/metadata-modules/permissions/permissions.exception.ts
|
||||
msgid "Invalid argument provided."
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user