diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql index c069c25d61..7f68021dbc 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql @@ -3294,7 +3294,6 @@ type Mutation { updateWorkspace(data: UpdateWorkspaceInput!): Workspace! deleteCurrentWorkspace: Workspace! checkCustomDomainValidRecords: DomainValidRecords - installApplication(appRegistrationId: String!, version: String): Boolean! runWorkspaceMigration(workspaceMigration: WorkspaceMigrationInput!): Boolean! uninstallApplication(universalIdentifier: String!): Boolean! createOIDCIdentityProvider(input: SetupOIDCSsoInput!): SetupSso! @@ -3315,7 +3314,8 @@ type Mutation { deleteEmailingDomain(id: String!): Boolean! verifyEmailingDomain(id: String!): EmailingDomain! createOneAppToken(input: CreateOneAppTokenInput!): AppToken! - installMarketplaceApp(universalIdentifier: String!, version: String): Application! + installMarketplaceApp(universalIdentifier: String!, version: String): Boolean! @deprecated(reason: "Use installApplication instead") + installApplication(universalIdentifier: String!, version: String): Application! syncMarketplaceCatalog: Boolean! createDevelopmentApplication(universalIdentifier: String!, name: String!): DevelopmentApplication! generateApplicationToken(applicationId: UUID!): ApplicationTokenPair! diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts index 389bccfe35..54343e6f71 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts @@ -2827,7 +2827,6 @@ export interface Mutation { updateWorkspace: Workspace deleteCurrentWorkspace: Workspace checkCustomDomainValidRecords?: DomainValidRecords - installApplication: Scalars['Boolean'] runWorkspaceMigration: Scalars['Boolean'] uninstallApplication: Scalars['Boolean'] createOIDCIdentityProvider: SetupSso @@ -2848,7 +2847,9 @@ export interface Mutation { deleteEmailingDomain: Scalars['Boolean'] verifyEmailingDomain: EmailingDomain createOneAppToken: AppToken - installMarketplaceApp: Application + /** @deprecated Use installApplication instead */ + installMarketplaceApp: Scalars['Boolean'] + installApplication: Application syncMarketplaceCatalog: Scalars['Boolean'] createDevelopmentApplication: DevelopmentApplication generateApplicationToken: ApplicationTokenPair @@ -5879,7 +5880,6 @@ export interface MutationGenqlSelection{ updateWorkspace?: (WorkspaceGenqlSelection & { __args: {data: UpdateWorkspaceInput} }) deleteCurrentWorkspace?: WorkspaceGenqlSelection checkCustomDomainValidRecords?: DomainValidRecordsGenqlSelection - installApplication?: { __args: {appRegistrationId: Scalars['String'], version?: (Scalars['String'] | null)} } runWorkspaceMigration?: { __args: {workspaceMigration: WorkspaceMigrationInput} } uninstallApplication?: { __args: {universalIdentifier: Scalars['String']} } createOIDCIdentityProvider?: (SetupSsoGenqlSelection & { __args: {input: SetupOIDCSsoInput} }) @@ -5900,7 +5900,9 @@ export interface MutationGenqlSelection{ deleteEmailingDomain?: { __args: {id: Scalars['String']} } verifyEmailingDomain?: (EmailingDomainGenqlSelection & { __args: {id: Scalars['String']} }) createOneAppToken?: (AppTokenGenqlSelection & { __args: {input: CreateOneAppTokenInput} }) - installMarketplaceApp?: (ApplicationGenqlSelection & { __args: {universalIdentifier: Scalars['String'], version?: (Scalars['String'] | null)} }) + /** @deprecated Use installApplication instead */ + installMarketplaceApp?: { __args: {universalIdentifier: Scalars['String'], version?: (Scalars['String'] | null)} } + installApplication?: (ApplicationGenqlSelection & { __args: {universalIdentifier: Scalars['String'], version?: (Scalars['String'] | null)} }) syncMarketplaceCatalog?: boolean | number createDevelopmentApplication?: (DevelopmentApplicationGenqlSelection & { __args: {universalIdentifier: Scalars['String'], name: Scalars['String']} }) generateApplicationToken?: (ApplicationTokenPairGenqlSelection & { __args: {applicationId: Scalars['UUID']} }) diff --git a/packages/twenty-client-sdk/src/metadata/generated/types.ts b/packages/twenty-client-sdk/src/metadata/generated/types.ts index 0c6d7d0d69..a5af84f43b 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/types.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/types.ts @@ -8388,18 +8388,6 @@ export default { "checkCustomDomainValidRecords": [ 228 ], - "installApplication": [ - 6, - { - "appRegistrationId": [ - 1, - "String!" - ], - "version": [ - 1 - ] - } - ], "runWorkspaceMigration": [ 6, { @@ -8602,6 +8590,18 @@ export default { } ], "installMarketplaceApp": [ + 6, + { + "universalIdentifier": [ + 1, + "String!" + ], + "version": [ + 1 + ] + } + ], + "installApplication": [ 58, { "universalIdentifier": [ diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index a70107c8ad..f3ced00544 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -2457,8 +2457,9 @@ export type Mutation = { impersonate: Impersonate; initiateOTPProvisioning: InitiateTwoFactorAuthenticationProvisioning; initiateOTPProvisioningForAuthenticatedUser: InitiateTwoFactorAuthenticationProvisioning; - installApplication: Scalars['Boolean']; - installMarketplaceApp: Application; + installApplication: Application; + /** @deprecated Use installApplication instead */ + installMarketplaceApp: Scalars['Boolean']; refreshEnterpriseValidityToken: Scalars['Boolean']; removeQueryFromEventStream: Scalars['Boolean']; removeRoleFromAgent: Scalars['Boolean']; @@ -3087,7 +3088,7 @@ export type MutationInitiateOtpProvisioningArgs = { export type MutationInstallApplicationArgs = { - appRegistrationId: Scalars['String']; + universalIdentifier: Scalars['String']; version?: InputMaybe; }; @@ -6620,13 +6621,12 @@ export type MarketplaceAppDetailFieldsFragment = { __typename?: 'MarketplaceAppD export type MarketplaceAppFieldsFragment = { __typename?: 'MarketplaceApp', id: string, name: string, description: string, author: string, category: string, logo?: string | null, sourcePackage?: string | null, isFeatured: boolean }; -export type InstallMarketplaceAppMutationVariables = Exact<{ +export type InstallApplicationMutationVariables = Exact<{ universalIdentifier: Scalars['String']; - version?: InputMaybe; }>; -export type InstallMarketplaceAppMutation = { __typename?: 'Mutation', installMarketplaceApp: { __typename?: 'Application', id: string } }; +export type InstallApplicationMutation = { __typename?: 'Mutation', installApplication: { __typename?: 'Application', id: string } }; export type UpgradeApplicationMutationVariables = Exact<{ appRegistrationId: Scalars['String']; @@ -8014,7 +8014,7 @@ export const FindManyAvailablePackagesDocument = {"kind":"Document","definitions export const FindManyLogicFunctionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindManyLogicFunctions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findManyLogicFunctions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LogicFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LogicFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LogicFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"timeoutSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"sourceHandlerPath"}},{"kind":"Field","name":{"kind":"Name","value":"handlerName"}},{"kind":"Field","name":{"kind":"Name","value":"cronTriggerSettings"}},{"kind":"Field","name":{"kind":"Name","value":"databaseEventTriggerSettings"}},{"kind":"Field","name":{"kind":"Name","value":"httpRouteTriggerSettings"}},{"kind":"Field","name":{"kind":"Name","value":"toolTriggerSettings"}},{"kind":"Field","name":{"kind":"Name","value":"workflowActionTriggerSettings"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const FindOneLogicFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindOneLogicFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"LogicFunctionIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findOneLogicFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LogicFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LogicFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LogicFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"timeoutSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"sourceHandlerPath"}},{"kind":"Field","name":{"kind":"Name","value":"handlerName"}},{"kind":"Field","name":{"kind":"Name","value":"cronTriggerSettings"}},{"kind":"Field","name":{"kind":"Name","value":"databaseEventTriggerSettings"}},{"kind":"Field","name":{"kind":"Name","value":"httpRouteTriggerSettings"}},{"kind":"Field","name":{"kind":"Name","value":"toolTriggerSettings"}},{"kind":"Field","name":{"kind":"Name","value":"workflowActionTriggerSettings"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const GetLogicFunctionSourceCodeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetLogicFunctionSourceCode"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"LogicFunctionIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getLogicFunctionSourceCode"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; -export const InstallMarketplaceAppDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InstallMarketplaceApp"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"version"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"installMarketplaceApp"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"universalIdentifier"},"value":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}}},{"kind":"Argument","name":{"kind":"Name","value":"version"},"value":{"kind":"Variable","name":{"kind":"Name","value":"version"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; +export const InstallApplicationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InstallApplication"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"installApplication"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"universalIdentifier"},"value":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; export const UpgradeApplicationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpgradeApplication"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appRegistrationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"targetVersion"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"upgradeApplication"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appRegistrationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appRegistrationId"}}},{"kind":"Argument","name":{"kind":"Name","value":"targetVersion"},"value":{"kind":"Variable","name":{"kind":"Name","value":"targetVersion"}}}]}]}}]} as unknown as DocumentNode; export const FindManyMarketplaceAppsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindManyMarketplaceApps"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findManyMarketplaceApps"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"MarketplaceAppFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MarketplaceAppFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MarketplaceApp"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"author"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"sourcePackage"}},{"kind":"Field","name":{"kind":"Name","value":"isFeatured"}}]}}]} as unknown as DocumentNode; export const FindMarketplaceAppDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindMarketplaceAppDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findMarketplaceAppDetail"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"universalIdentifier"},"value":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"MarketplaceAppDetailFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MarketplaceAppDetailFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MarketplaceAppDetail"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sourceType"}},{"kind":"Field","name":{"kind":"Name","value":"sourcePackage"}},{"kind":"Field","name":{"kind":"Name","value":"latestAvailableVersion"}},{"kind":"Field","name":{"kind":"Name","value":"isListed"}},{"kind":"Field","name":{"kind":"Name","value":"isFeatured"}},{"kind":"Field","name":{"kind":"Name","value":"manifest"}}]}}]} as unknown as DocumentNode; diff --git a/packages/twenty-front/src/modules/marketplace/graphql/mutations/installApplication.ts b/packages/twenty-front/src/modules/marketplace/graphql/mutations/installApplication.ts new file mode 100644 index 0000000000..4f3fa0b5c8 --- /dev/null +++ b/packages/twenty-front/src/modules/marketplace/graphql/mutations/installApplication.ts @@ -0,0 +1,9 @@ +import gql from 'graphql-tag'; + +export const INSTALL_APPLICATION = gql` + mutation InstallApplication($universalIdentifier: String!) { + installApplication(universalIdentifier: $universalIdentifier) { + id + } + } +`; diff --git a/packages/twenty-front/src/modules/marketplace/graphql/mutations/installMarketplaceApp.ts b/packages/twenty-front/src/modules/marketplace/graphql/mutations/installMarketplaceApp.ts deleted file mode 100644 index af96c428a6..0000000000 --- a/packages/twenty-front/src/modules/marketplace/graphql/mutations/installMarketplaceApp.ts +++ /dev/null @@ -1,15 +0,0 @@ -import gql from 'graphql-tag'; - -export const INSTALL_MARKETPLACE_APP = gql` - mutation InstallMarketplaceApp( - $universalIdentifier: String! - $version: String - ) { - installMarketplaceApp( - universalIdentifier: $universalIdentifier - version: $version - ) { - id - } - } -`; diff --git a/packages/twenty-front/src/modules/marketplace/hooks/useInstallMarketplaceApp.ts b/packages/twenty-front/src/modules/marketplace/hooks/useInstallMarketplaceApp.ts index d24d9e7130..0430587f75 100644 --- a/packages/twenty-front/src/modules/marketplace/hooks/useInstallMarketplaceApp.ts +++ b/packages/twenty-front/src/modules/marketplace/hooks/useInstallMarketplaceApp.ts @@ -4,25 +4,23 @@ import { t } from '@lingui/core/macro'; import { useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { - InstallMarketplaceAppDocument, - type InstallMarketplaceAppMutation, + InstallApplicationDocument, + type InstallApplicationMutation, } from '~/generated-metadata/graphql'; export const useInstallMarketplaceApp = () => { const { enqueueErrorSnackBar, enqueueSuccessSnackBar } = useSnackBar(); const [isInstalling, setIsInstalling] = useState(false); - const [installMarketplaceAppMutation] = useMutation( - InstallMarketplaceAppDocument, - ); + const [installApplicationMutation] = useMutation(InstallApplicationDocument); const install = async (variables: { universalIdentifier: string; version?: string; - }): Promise => { + }): Promise => { setIsInstalling(true); try { - const result = await installMarketplaceAppMutation({ variables }); + const result = await installApplicationMutation({ variables }); if (isDefined(result.data)) { enqueueSuccessSnackBar({ diff --git a/packages/twenty-front/src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx b/packages/twenty-front/src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx index aa198c8538..9825ee3da7 100644 --- a/packages/twenty-front/src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx +++ b/packages/twenty-front/src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx @@ -104,7 +104,7 @@ export const SettingsAvailableApplicationDetails = () => { universalIdentifier: detail.universalIdentifier, }); - const applicationId = data?.installMarketplaceApp?.id; + const applicationId = data?.installApplication?.id; if (isDefined(applicationId)) { navigateSettings(SettingsPath.ApplicationDetail, { diff --git a/packages/twenty-sdk/src/cli/utilities/api/file-api.ts b/packages/twenty-sdk/src/cli/utilities/api/file-api.ts index 772f23acb3..2e6f486774 100644 --- a/packages/twenty-sdk/src/cli/utilities/api/file-api.ts +++ b/packages/twenty-sdk/src/cli/utilities/api/file-api.ts @@ -139,8 +139,8 @@ export class FileApi { }): Promise> { try { const mutation = ` - mutation InstallMarketplaceApp($universalIdentifier: String!) { - installMarketplaceApp(universalIdentifier: $universalIdentifier) { + mutation InstallApplication($universalIdentifier: String!) { + installApplication(universalIdentifier: $universalIdentifier) { id } } @@ -169,7 +169,7 @@ export class FileApi { return { success: true, - data: response.data.data.installMarketplaceApp, + data: response.data.data.installApplication, }; } catch (error) { if (axios.isAxiosError(error) && error.response) { diff --git a/packages/twenty-server/src/engine/core-modules/application/application-install/application-install.resolver.ts b/packages/twenty-server/src/engine/core-modules/application/application-install/application-install.resolver.ts index 85d5d1d2ae..ebacfa62e8 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-install/application-install.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-install/application-install.resolver.ts @@ -4,14 +4,13 @@ import { UseInterceptors, UsePipes, } from '@nestjs/common'; -import { Args, Mutation, Query } from '@nestjs/graphql'; +import { Args, Query } from '@nestjs/graphql'; import { PermissionFlagType } from 'twenty-shared/constants'; import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; import { ApplicationExceptionFilter } from 'src/engine/core-modules/application/application-exception-filter'; import { ApplicationService } from 'src/engine/core-modules/application/application.service'; -import { ApplicationInstallService } from 'src/engine/core-modules/application/application-install/application-install.service'; import { ApplicationDTO } from 'src/engine/core-modules/application/dtos/application.dto'; import { AuthGraphqlApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter'; import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; @@ -27,10 +26,7 @@ import { WorkspaceMigrationGraphqlApiExceptionInterceptor } from 'src/engine/wor @UseInterceptors(WorkspaceMigrationGraphqlApiExceptionInterceptor) @UseGuards(WorkspaceAuthGuard) export class ApplicationInstallResolver { - constructor( - private readonly applicationService: ApplicationService, - private readonly applicationInstallService: ApplicationInstallService, - ) {} + constructor(private readonly applicationService: ApplicationService) {} @Query(() => [ApplicationDTO]) @UseGuards(SettingsPermissionGuard(PermissionFlagType.APPLICATIONS)) @@ -57,19 +53,4 @@ export class ApplicationInstallResolver { workspaceId, }); } - - @Mutation(() => Boolean) - @UseGuards(SettingsPermissionGuard(PermissionFlagType.APPLICATIONS)) - async installApplication( - @Args('appRegistrationId') appRegistrationId: string, - @Args('version', { type: () => String, nullable: true }) - version: string | undefined, - @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, - ): Promise { - return this.applicationInstallService.installApplication({ - appRegistrationId, - version, - workspaceId, - }); - } } diff --git a/packages/twenty-server/src/engine/core-modules/application/application-marketplace/marketplace.resolver.ts b/packages/twenty-server/src/engine/core-modules/application/application-marketplace/marketplace.resolver.ts index a0b121a9b5..b1082977fb 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-marketplace/marketplace.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-marketplace/marketplace.resolver.ts @@ -48,13 +48,37 @@ export class MarketplaceResolver { ); } - @Mutation(() => ApplicationDTO) + @Mutation(() => Boolean, { + deprecationReason: 'Use installApplication instead', + }) @UseGuards(SettingsPermissionGuard(PermissionFlagType.MARKETPLACE_APPS)) async installMarketplaceApp( @Args('universalIdentifier') universalIdentifier: string, @Args('version', { type: () => String, nullable: true }) version: string | undefined, @AuthWorkspace() workspace: WorkspaceEntity, + ): Promise { + const registration = + await this.marketplaceQueryService.findRegistrationByUniversalIdentifier( + universalIdentifier, + ); + + await this.applicationInstallService.installApplication({ + appRegistrationId: registration.id, + version, + workspaceId: workspace.id, + }); + + return true; + } + + @Mutation(() => ApplicationDTO) + @UseGuards(SettingsPermissionGuard(PermissionFlagType.MARKETPLACE_APPS)) + async installApplication( + @Args('universalIdentifier') universalIdentifier: string, + @Args('version', { type: () => String, nullable: true }) + version: string | undefined, + @AuthWorkspace() workspace: WorkspaceEntity, ) { const registration = await this.marketplaceQueryService.findRegistrationByUniversalIdentifier( diff --git a/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/failing-install-application.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/failing-install-application.integration-spec.ts.snap index 3f0787dc1c..a33e5c1501 100644 --- a/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/failing-install-application.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/failing-install-application.integration-spec.ts.snap @@ -1,16 +1,4 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`Install application should fail when entity does not exist should fail with execution error when installing non-existent app registration 1`] = ` -{ - "extensions": { - "code": "NOT_FOUND", - "subCode": "APPLICATION_NOT_FOUND", - "userFriendlyMessage": "Application not found.", - }, - "message": "Application registration with id 20202020-0000-0000-0000-000000000000 not found", - "name": "NotFoundError", -} -`; +// Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Install application should fail when entity does not exist should fail when a role has an invalid universalIdentifier 1`] = ` { @@ -46,3 +34,15 @@ exports[`Install application should fail when entity does not exist should fail "name": "GraphQLError", } `; + +exports[`Install application should fail when entity does not exist should fail with execution error when installing non-existent app registration 1`] = ` +{ + "extensions": { + "code": "NOT_FOUND", + "subCode": "APPLICATION_REGISTRATION_NOT_FOUND", + "userFriendlyMessage": "Application registration not found.", + }, + "message": "No application registration found for identifier "20202020-0000-0000-0000-000000000000"", + "name": "NotFoundError", +} +`; diff --git a/packages/twenty-server/test/integration/metadata/suites/application/failing-install-application-validation-errors.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/application/failing-install-application-validation-errors.integration-spec.ts index abd2229338..c6884ab370 100644 --- a/packages/twenty-server/test/integration/metadata/suites/application/failing-install-application-validation-errors.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/application/failing-install-application-validation-errors.integration-spec.ts @@ -141,13 +141,11 @@ describe('Install application should return structured validation errors', () => expect(uploadResult.errors).toBeUndefined(); expect(uploadResult.data?.uploadAppTarball.id).toBeDefined(); - const registrationId = uploadResult.data!.uploadAppTarball.id; - createdApplicationUniversalIdentifiers.push(universalIdentifier); const { errors } = await installApplication({ input: { - appRegistrationId: registrationId, + universalIdentifier, }, expectToFail: true, }); diff --git a/packages/twenty-server/test/integration/metadata/suites/application/failing-install-application.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/application/failing-install-application.integration-spec.ts index df05ca87f1..8e8ce15fa4 100644 --- a/packages/twenty-server/test/integration/metadata/suites/application/failing-install-application.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/application/failing-install-application.integration-spec.ts @@ -29,7 +29,7 @@ describe('Install application should fail when entity does not exist', () => { const { errors } = await installApplication({ expectToFail: true, input: { - appRegistrationId: '20202020-0000-0000-0000-000000000000', + universalIdentifier: '20202020-0000-0000-0000-000000000000', }, }); diff --git a/packages/twenty-server/test/integration/metadata/suites/application/marketplace-catalog-sync.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/application/marketplace-catalog-sync.integration-spec.ts index dba4588128..1647776340 100644 --- a/packages/twenty-server/test/integration/metadata/suites/application/marketplace-catalog-sync.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/application/marketplace-catalog-sync.integration-spec.ts @@ -1,6 +1,11 @@ import crypto from 'crypto'; +import { promises as fs } from 'fs'; +import { tmpdir } from 'os'; +import { join } from 'path'; import request from 'supertest'; +import * as tar from 'tar'; +import { cleanupApplicationAndAppRegistration } from 'test/integration/metadata/suites/application/utils/cleanup-application-and-app-registration.util'; import { type DataSource } from 'typeorm'; const TEST_WORKSPACE_ID = '20202020-1c25-4d02-bf25-6aeccf7ea419'; @@ -21,8 +26,8 @@ const MARKETPLACE_QUERY = ` `; const INSTALL_MUTATION = ` - mutation InstallMarketplaceApp($universalIdentifier: String!) { - installMarketplaceApp(universalIdentifier: $universalIdentifier) { + mutation InstallApplication($universalIdentifier: String!) { + installApplication(universalIdentifier: $universalIdentifier) { id } } @@ -178,7 +183,7 @@ describe('Marketplace Catalog Sync (integration)', () => { }); }); - describe('installMarketplaceApp', () => { + describe('installApplication', () => { it('should fail if registration does not exist', async () => { const res = await gqlRequest(INSTALL_MUTATION, { universalIdentifier: crypto.randomUUID(), @@ -189,5 +194,106 @@ describe('Marketplace Catalog Sync (integration)', () => { 'No application registration found', ); }); + + it('should install a tarball app and return the application id', async () => { + const universalIdentifier = crypto.randomUUID(); + const roleId = crypto.randomUUID(); + + const manifest = JSON.stringify({ + application: { + universalIdentifier, + displayName: 'Install Test App', + description: 'App for testing installApplication', + icon: 'IconTestPipe', + defaultRoleUniversalIdentifier: roleId, + applicationVariables: {}, + packageJsonChecksum: null, + yarnLockChecksum: null, + }, + roles: [ + { + universalIdentifier: roleId, + label: 'Default Role', + description: 'Default role', + }, + ], + skills: [], + agents: [], + objects: [], + fields: [], + logicFunctions: [], + frontComponents: [], + publicAssets: [], + views: [], + navigationMenuItems: [], + pageLayouts: [], + pageLayoutTabs: [], + commandMenuItems: [], + }); + + const packageJson = JSON.stringify({ + name: 'test-install-app', + version: '1.0.0', + }); + + const tempId = crypto.randomUUID(); + const sourceDir = join(tmpdir(), `test-tarball-src-${tempId}`); + const tarballPath = join(tmpdir(), `test-tarball-${tempId}.tar.gz`); + + await fs.mkdir(sourceDir, { recursive: true }); + await fs.writeFile(join(sourceDir, 'manifest.json'), manifest); + await fs.writeFile(join(sourceDir, 'package.json'), packageJson); + + await tar.create( + { file: tarballPath, gzip: true, cwd: sourceDir }, + ['manifest.json', 'package.json'], + ); + + const tarballBuffer = await fs.readFile(tarballPath); + + await fs.rm(sourceDir, { recursive: true, force: true }); + await fs.rm(tarballPath, { force: true }); + + const UPLOAD_MUTATION = ` + mutation UploadAppTarball($file: Upload!, $universalIdentifier: String) { + uploadAppTarball(file: $file, universalIdentifier: $universalIdentifier) { + id + universalIdentifier + name + } + } + `; + + const uploadRes = await request(baseUrl) + .post('/metadata') + .set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`) + .field( + 'operations', + JSON.stringify({ + query: UPLOAD_MUTATION, + variables: { file: null, universalIdentifier }, + }), + ) + .field('map', JSON.stringify({ '0': ['variables.file'] })) + .attach('0', tarballBuffer, 'app.tar.gz') + .expect(200); + + expect(uploadRes.body.errors).toBeUndefined(); + expect(uploadRes.body.data.uploadAppTarball.id).toBeDefined(); + + try { + const installRes = await gqlRequest(INSTALL_MUTATION, { + universalIdentifier, + }).expect(200); + + expect(installRes.body.errors).toBeUndefined(); + expect(installRes.body.data.installApplication).toBeDefined(); + expect(installRes.body.data.installApplication.id).toBeDefined(); + } finally { + await cleanupApplicationAndAppRegistration({ + applicationUniversalIdentifier: universalIdentifier, + }); + } + }, 120000); }); }); diff --git a/packages/twenty-server/test/integration/metadata/suites/application/utils/install-application-query-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/application/utils/install-application-query-factory.util.ts index b9996c1272..cdfded9348 100644 --- a/packages/twenty-server/test/integration/metadata/suites/application/utils/install-application-query-factory.util.ts +++ b/packages/twenty-server/test/integration/metadata/suites/application/utils/install-application-query-factory.util.ts @@ -1,7 +1,7 @@ import gql from 'graphql-tag'; export type InstallApplicationFactoryInput = { - appRegistrationId: string; + universalIdentifier: string; version?: string; }; @@ -11,15 +11,20 @@ export const installApplicationQueryFactory = ({ input: InstallApplicationFactoryInput; }) => ({ query: gql` - mutation InstallApplication($appRegistrationId: String!, $version: String) { + mutation InstallApplication( + $universalIdentifier: String! + $version: String + ) { installApplication( - appRegistrationId: $appRegistrationId + universalIdentifier: $universalIdentifier version: $version - ) + ) { + id + } } `, variables: { - appRegistrationId: input.appRegistrationId, + universalIdentifier: input.universalIdentifier, version: input.version, }, }); diff --git a/packages/twenty-server/test/integration/metadata/suites/application/utils/install-application.util.ts b/packages/twenty-server/test/integration/metadata/suites/application/utils/install-application.util.ts index 314643d2fd..9ad9c87347 100644 --- a/packages/twenty-server/test/integration/metadata/suites/application/utils/install-application.util.ts +++ b/packages/twenty-server/test/integration/metadata/suites/application/utils/install-application.util.ts @@ -16,7 +16,7 @@ export const installApplication = async ({ expectToFail?: boolean; token?: string; }): CommonResponseBody<{ - installApplication: boolean; + installApplication: { id: string }; }> => { const graphqlOperation = installApplicationQueryFactory({ input });