Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ff2e3d990 | |||
| 822637da1f |
@@ -1001,6 +1001,7 @@ export type CreateFrontComponentInput = {
|
||||
builtComponentPath: Scalars['String'];
|
||||
componentName: Scalars['String'];
|
||||
description?: InputMaybe<Scalars['String']>;
|
||||
fileId?: InputMaybe<Scalars['UUID']>;
|
||||
id?: InputMaybe<Scalars['UUID']>;
|
||||
name: Scalars['String'];
|
||||
sourceComponentPath: Scalars['String'];
|
||||
@@ -1673,6 +1674,7 @@ export type FrontComponent = {
|
||||
componentName: Scalars['String'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
description?: Maybe<Scalars['String']>;
|
||||
fileId?: Maybe<Scalars['UUID']>;
|
||||
id: Scalars['UUID'];
|
||||
name: Scalars['String'];
|
||||
sourceComponentPath: Scalars['String'];
|
||||
@@ -4713,6 +4715,7 @@ export type UpdateFrontComponentInput = {
|
||||
|
||||
export type UpdateFrontComponentInputUpdates = {
|
||||
description?: InputMaybe<Scalars['String']>;
|
||||
fileId?: InputMaybe<Scalars['UUID']>;
|
||||
name?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
|
||||
@@ -997,6 +997,7 @@ export type CreateFrontComponentInput = {
|
||||
builtComponentPath: Scalars['String'];
|
||||
componentName: Scalars['String'];
|
||||
description?: InputMaybe<Scalars['String']>;
|
||||
fileId?: InputMaybe<Scalars['UUID']>;
|
||||
id?: InputMaybe<Scalars['UUID']>;
|
||||
name: Scalars['String'];
|
||||
sourceComponentPath: Scalars['String'];
|
||||
@@ -1645,6 +1646,7 @@ export type FrontComponent = {
|
||||
componentName: Scalars['String'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
description?: Maybe<Scalars['String']>;
|
||||
fileId?: Maybe<Scalars['UUID']>;
|
||||
id: Scalars['UUID'];
|
||||
name: Scalars['String'];
|
||||
sourceComponentPath: Scalars['String'];
|
||||
@@ -4551,6 +4553,7 @@ export type UpdateFrontComponentInput = {
|
||||
|
||||
export type UpdateFrontComponentInputUpdates = {
|
||||
description?: InputMaybe<Scalars['String']>;
|
||||
fileId?: InputMaybe<Scalars['UUID']>;
|
||||
name?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
|
||||
@@ -525,7 +525,7 @@ export class ApiService {
|
||||
builtHandlerPath: string;
|
||||
fileFolder: FileFolder;
|
||||
applicationUniversalIdentifier: string;
|
||||
}): Promise<ApiResponse<boolean>> {
|
||||
}): Promise<ApiResponse<{ id: string; path: string }>> {
|
||||
try {
|
||||
const absolutePath = path.resolve(filePath);
|
||||
|
||||
@@ -543,7 +543,7 @@ export class ApiService {
|
||||
const mutation = `
|
||||
mutation UploadApplicationFile($file: Upload!, $applicationUniversalIdentifier: String!, $fileFolder: FileFolder!, $filePath: String!) {
|
||||
uploadApplicationFile(file: $file, applicationUniversalIdentifier: $applicationUniversalIdentifier, fileFolder: $fileFolder, filePath: $filePath)
|
||||
{ path }
|
||||
{ id path }
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -14,7 +14,12 @@ export type UpdateManifestChecksumParams = {
|
||||
manifest: Manifest;
|
||||
builtFileInfos: Map<
|
||||
string,
|
||||
{ checksum: string; builtPath: string; fileFolder: FileFolder }
|
||||
{
|
||||
checksum: string;
|
||||
builtPath: string;
|
||||
fileFolder: FileFolder;
|
||||
fileId?: string;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
@@ -25,7 +30,7 @@ export const manifestUpdateChecksums = ({
|
||||
let result = structuredClone(manifest);
|
||||
for (const [
|
||||
builtPath,
|
||||
{ fileFolder, checksum },
|
||||
{ fileFolder, checksum, fileId },
|
||||
] of builtFileInfos.entries()) {
|
||||
const rootBuiltPath = relative(OUTPUT_DIR, builtPath);
|
||||
if (fileFolder === FileFolder.BuiltLogicFunction) {
|
||||
@@ -72,7 +77,11 @@ export const manifestUpdateChecksums = ({
|
||||
...result,
|
||||
frontComponents: frontComponents.map((component, index) =>
|
||||
index === componentIndex
|
||||
? { ...component, builtComponentChecksum: checksum }
|
||||
? {
|
||||
...component,
|
||||
builtComponentChecksum: checksum,
|
||||
...(fileId ? { fileId } : {}),
|
||||
}
|
||||
: component,
|
||||
),
|
||||
};
|
||||
|
||||
@@ -30,6 +30,7 @@ export class DevModeOrchestrator {
|
||||
builtPath: string;
|
||||
sourcePath: string;
|
||||
fileFolder: FileFolder;
|
||||
fileId?: string;
|
||||
}
|
||||
>();
|
||||
|
||||
@@ -175,6 +176,12 @@ export class DevModeOrchestrator {
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.success) {
|
||||
const existingInfo = this.builtFileInfos.get(builtPath);
|
||||
|
||||
if (existingInfo) {
|
||||
existingInfo.fileId = result.data.id;
|
||||
}
|
||||
|
||||
this.uiStateManager.addEvent({
|
||||
message: `Successfully uploaded ${builtPath}`,
|
||||
status: 'success',
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { type MigrationInterface, type QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddFileRelationToFrontComponent1770742116157
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'AddFileRelationToFrontComponent1770742116157';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."frontComponent" ADD "fileId" uuid`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."frontComponent" ADD CONSTRAINT "UQ_fd86790edb444e6f6e36e021b64" UNIQUE ("fileId")`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."frontComponent" ADD CONSTRAINT "FK_fd86790edb444e6f6e36e021b64" FOREIGN KEY ("fileId") REFERENCES "core"."file"("id") ON DELETE RESTRICT ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."frontComponent" DROP CONSTRAINT "FK_fd86790edb444e6f6e36e021b64"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."frontComponent" DROP CONSTRAINT "UQ_fd86790edb444e6f6e36e021b64"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."frontComponent" DROP COLUMN "fileId"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+1
@@ -59,6 +59,7 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
|
||||
"sourceComponentPath",
|
||||
"builtComponentPath",
|
||||
"componentName",
|
||||
"fileId",
|
||||
],
|
||||
"propertiesToStringify": [],
|
||||
},
|
||||
|
||||
+1
@@ -341,6 +341,7 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
|
||||
sourceComponentPath: { toStringify: false, universalProperty: undefined },
|
||||
builtComponentPath: { toStringify: false, universalProperty: undefined },
|
||||
componentName: { toStringify: false, universalProperty: undefined },
|
||||
fileId: { toStringify: false, universalProperty: undefined },
|
||||
},
|
||||
webhook: {
|
||||
targetUrl: { toStringify: false, universalProperty: undefined },
|
||||
|
||||
+1
@@ -494,6 +494,7 @@ export const ALL_METADATA_RELATIONS = {
|
||||
manyToOne: {
|
||||
workspace: null,
|
||||
application: null,
|
||||
file: null,
|
||||
},
|
||||
oneToMany: {},
|
||||
},
|
||||
|
||||
+1
@@ -7,4 +7,5 @@ export const FLAT_FRONT_COMPONENT_EDITABLE_PROPERTIES = [
|
||||
'sourceComponentPath',
|
||||
'builtComponentPath',
|
||||
'componentName',
|
||||
'fileId',
|
||||
] as const satisfies MetadataEntityPropertyName<'frontComponent'>[];
|
||||
|
||||
+1
@@ -33,6 +33,7 @@ export const fromCreateFrontComponentInputToFlatFrontComponentToCreate = ({
|
||||
builtComponentPath: createFrontComponentInput.builtComponentPath,
|
||||
componentName: createFrontComponentInput.componentName,
|
||||
builtComponentChecksum: createFrontComponentInput.builtComponentChecksum,
|
||||
fileId: createFrontComponentInput.fileId ?? null,
|
||||
workspaceId,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
|
||||
+3
@@ -15,6 +15,9 @@ export const fromFlatFrontComponentToFrontComponentDto = (
|
||||
builtComponentPath: flatFrontComponent.builtComponentPath,
|
||||
componentName: flatFrontComponent.componentName,
|
||||
builtComponentChecksum: flatFrontComponent.builtComponentChecksum,
|
||||
fileId: isDefined(flatFrontComponent.fileId)
|
||||
? flatFrontComponent.fileId
|
||||
: undefined,
|
||||
universalIdentifier: isDefined(flatFrontComponent.universalIdentifier)
|
||||
? flatFrontComponent.universalIdentifier
|
||||
: undefined,
|
||||
|
||||
+1
@@ -31,6 +31,7 @@ export const fromFrontComponentEntityToFlatFrontComponent = ({
|
||||
builtComponentPath: frontComponentEntity.builtComponentPath,
|
||||
componentName: frontComponentEntity.componentName,
|
||||
builtComponentChecksum: frontComponentEntity.builtComponentChecksum,
|
||||
fileId: frontComponentEntity.fileId,
|
||||
workspaceId: frontComponentEntity.workspaceId,
|
||||
universalIdentifier: frontComponentEntity.universalIdentifier,
|
||||
applicationId: frontComponentEntity.applicationId,
|
||||
|
||||
+5
@@ -44,4 +44,9 @@ export class CreateFrontComponentInput {
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
builtComponentChecksum: string;
|
||||
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
fileId?: string;
|
||||
}
|
||||
|
||||
+5
@@ -43,6 +43,11 @@ export class FrontComponentDTO {
|
||||
@Field()
|
||||
builtComponentChecksum: string;
|
||||
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
fileId?: string;
|
||||
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
|
||||
+5
@@ -27,6 +27,11 @@ export class UpdateFrontComponentInputUpdates {
|
||||
@IsString()
|
||||
@HideField()
|
||||
builtComponentChecksum?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsUUID()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
fileId?: string;
|
||||
}
|
||||
|
||||
@InputType()
|
||||
|
||||
+11
@@ -2,10 +2,14 @@ import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
OneToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
Relation,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity';
|
||||
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
|
||||
|
||||
@Entity('frontComponent')
|
||||
@@ -34,6 +38,13 @@ export class FrontComponentEntity
|
||||
@Column({ nullable: false })
|
||||
builtComponentChecksum: string;
|
||||
|
||||
@Column({ nullable: true, type: 'uuid' })
|
||||
fileId: string | null;
|
||||
|
||||
@OneToOne(() => FileEntity, { onDelete: 'RESTRICT' })
|
||||
@JoinColumn({ name: 'fileId' })
|
||||
file: Relation<FileEntity> | null;
|
||||
|
||||
@CreateDateColumn({ type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
|
||||
+4
@@ -1,6 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
|
||||
import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { FlatFrontComponentModule } from 'src/engine/metadata-modules/flat-front-component/flat-front-component.module';
|
||||
import { FrontComponentController } from 'src/engine/metadata-modules/front-component/controllers/front-component.controller';
|
||||
@@ -14,6 +17,7 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([FileEntity, ApplicationEntity]),
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
WorkspaceMigrationModule,
|
||||
ApplicationModule,
|
||||
|
||||
+46
@@ -1,13 +1,18 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { type Readable } from 'stream';
|
||||
|
||||
import { FileFolder } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/services/application.service';
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service';
|
||||
import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity';
|
||||
import { removeFileFolderFromFileEntityPath } from 'src/engine/core-modules/file/utils/remove-file-folder-from-file-entity-path.utils';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
@@ -32,6 +37,10 @@ export class FrontComponentService {
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly applicationService: ApplicationService,
|
||||
private readonly fileStorageService: FileStorageService,
|
||||
@InjectRepository(FileEntity)
|
||||
private readonly fileRepository: Repository<FileEntity>,
|
||||
@InjectRepository(ApplicationEntity)
|
||||
private readonly applicationRepository: Repository<ApplicationEntity>,
|
||||
) {}
|
||||
|
||||
async findAll(workspaceId: string): Promise<FrontComponentDTO[]> {
|
||||
@@ -300,6 +309,13 @@ export class FrontComponentService {
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (isDefined(frontComponent.fileId)) {
|
||||
return this.getFileStreamByFileId({
|
||||
fileId: frontComponent.fileId,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
const application = await this.applicationService.findOneApplicationOrThrow(
|
||||
{
|
||||
id: frontComponent.applicationId,
|
||||
@@ -314,4 +330,34 @@ export class FrontComponentService {
|
||||
resourcePath: frontComponent.builtComponentPath,
|
||||
});
|
||||
}
|
||||
|
||||
private async getFileStreamByFileId({
|
||||
fileId,
|
||||
workspaceId,
|
||||
}: {
|
||||
fileId: string;
|
||||
workspaceId: string;
|
||||
}): Promise<Readable> {
|
||||
const file = await this.fileRepository.findOneOrFail({
|
||||
where: {
|
||||
id: fileId,
|
||||
workspaceId,
|
||||
application: { workspaceId },
|
||||
},
|
||||
});
|
||||
|
||||
const application = await this.applicationRepository.findOneOrFail({
|
||||
where: {
|
||||
id: file.applicationId,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
return this.fileStorageService.readFile({
|
||||
resourcePath: removeFileFolderFromFileEntityPath(file.path),
|
||||
fileFolder: FileFolder.BuiltFrontComponent,
|
||||
applicationUniversalIdentifier: application.universalIdentifier,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -504,6 +504,7 @@ export const ALL_UNIVERSAL_METADATA_RELATIONS = {
|
||||
manyToOne: {
|
||||
workspace: null,
|
||||
application: null,
|
||||
file: null,
|
||||
},
|
||||
oneToMany: {},
|
||||
},
|
||||
|
||||
+36
-2
@@ -1,12 +1,15 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { FileFolder } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service';
|
||||
import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity';
|
||||
import { FrontComponentEntity } from 'src/engine/metadata-modules/front-component/entities/front-component.entity';
|
||||
import {
|
||||
FrontComponentException,
|
||||
@@ -26,7 +29,11 @@ export class CreateFrontComponentActionHandlerService extends WorkspaceMigration
|
||||
'create',
|
||||
'frontComponent',
|
||||
) {
|
||||
constructor(private readonly fileStorageService: FileStorageService) {
|
||||
constructor(
|
||||
private readonly fileStorageService: FileStorageService,
|
||||
@InjectRepository(FileEntity)
|
||||
private readonly fileRepository: Repository<FileEntity>,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
@@ -54,7 +61,12 @@ export class CreateFrontComponentActionHandlerService extends WorkspaceMigration
|
||||
|
||||
const applicationUniversalIdentifier = flatApplication.universalIdentifier;
|
||||
|
||||
if (isDefined(frontComponent.builtComponentChecksum)) {
|
||||
if (isDefined(frontComponent.fileId)) {
|
||||
await this.verifyFileExistsByFileId({
|
||||
fileId: frontComponent.fileId,
|
||||
workspaceId,
|
||||
});
|
||||
} else if (isDefined(frontComponent.builtComponentChecksum)) {
|
||||
await this.verifySourceAndBuiltFilesExist({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
@@ -97,6 +109,28 @@ export class CreateFrontComponentActionHandlerService extends WorkspaceMigration
|
||||
}
|
||||
}
|
||||
|
||||
private async verifyFileExistsByFileId({
|
||||
fileId,
|
||||
workspaceId,
|
||||
}: {
|
||||
fileId: string;
|
||||
workspaceId: string;
|
||||
}): Promise<void> {
|
||||
const file = await this.fileRepository.findOne({
|
||||
where: {
|
||||
id: fileId,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!isDefined(file)) {
|
||||
throw new FrontComponentException(
|
||||
`Front component file with id ${fileId} not found`,
|
||||
FrontComponentExceptionCode.FRONT_COMPONENT_CREATE_FAILED,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async executeForWorkspaceSchema(
|
||||
_context: WorkspaceMigrationActionRunnerContext<FlatCreateFrontComponentAction>,
|
||||
): Promise<void> {
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity';
|
||||
import { WorkspaceSchemaManagerModule } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.module';
|
||||
import { CreateAgentActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/agent/services/create-agent-action-handler.service';
|
||||
import { DeleteAgentActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/agent/services/delete-agent-action-handler.service';
|
||||
@@ -72,7 +73,7 @@ import { UpdateViewActionHandlerService } from 'src/engine/workspace-manager/wor
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([ApplicationEntity]),
|
||||
TypeOrmModule.forFeature([ApplicationEntity, FileEntity]),
|
||||
WorkspaceSchemaManagerModule,
|
||||
],
|
||||
providers: [
|
||||
|
||||
@@ -6,4 +6,5 @@ export type FrontComponentManifest = {
|
||||
builtComponentPath: string;
|
||||
builtComponentChecksum: string;
|
||||
componentName: string;
|
||||
fileId?: string;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user