Skip to content

Commit f574c6f

Browse files
committed
Merge branch 'main' into fix/create-many-preserve-order
2 parents 5e59949 + cd7c286 commit f574c6f

File tree

17 files changed

+1101
-268
lines changed

17 files changed

+1101
-268
lines changed

packages/twenty-server/src/engine/api/common/common-query-runners/common-create-many-query-runner/common-create-many-query-runner.service.ts

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -432,20 +432,13 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
432432
.take(QUERY_MAX_RECORDS)
433433
.getMany();
434434

435-
// Preserve original input order by sorting results to match orderedIds
436-
const recordsById = new Map<string, ObjectRecord>(
437-
upsertedRecords.map((record) => [record.id, record as ObjectRecord]),
438-
);
439-
440-
return orderedIds.map((id) => {
441-
const record = recordsById.get(id);
435+
const orderIndex = new Map(orderedIds.map((id, index) => [id, index]));
442436

443-
if (!record) {
444-
throw new Error(`Record with id ${id} not found after upsert`);
445-
}
437+
upsertedRecords.sort(
438+
(a, b) => (orderIndex.get(a.id) ?? 0) - (orderIndex.get(b.id) ?? 0),
439+
);
446440

447-
return record;
448-
});
441+
return upsertedRecords as ObjectRecord[];
449442
}
450443

451444
async processQueryResult(

packages/twenty-server/src/engine/api/common/common-query-runners/common-find-duplicates-query-runner.service.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,13 @@ export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunne
7878
})
7979
.getMany()) as ObjectRecord[];
8080

81-
// Preserve original input order
82-
const recordsById = new Map(
83-
fetchedRecords.map((record) => [record.id, record]),
81+
const orderIndex = new Map(args.ids.map((id, index) => [id, index]));
82+
83+
fetchedRecords.sort(
84+
(a, b) => (orderIndex.get(a.id) ?? 0) - (orderIndex.get(b.id) ?? 0),
8485
);
8586

86-
objectRecords = args.ids
87-
.map((id) => recordsById.get(id))
88-
.filter(isDefined);
87+
objectRecords = fetchedRecords;
8988
} else if (args.data && !isEmpty(args.data)) {
9089
objectRecords = args.data;
9190
}

packages/twenty-server/src/engine/api/common/common-query-runners/common-merge-many-query-runner.service.ts

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,10 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
137137
flatFieldMetadataMaps: context.flatFieldMetadataMaps,
138138
});
139139

140-
const fetchedRecords = await context.repository.find({
140+
const fetchedRecords = (await context.repository.find({
141141
where: { id: In(args.ids) },
142142
select: columnsToSelect,
143-
});
143+
})) as ObjectRecord[];
144144

145145
if (fetchedRecords.length !== args.ids.length) {
146146
throw new CommonQueryRunnerException(
@@ -150,23 +150,13 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
150150
);
151151
}
152152

153-
// Preserve original input order
154-
const recordsById = new Map(
155-
fetchedRecords.map((record) => [record.id, record]),
153+
const orderIndex = new Map(args.ids.map((id, index) => [id, index]));
154+
155+
fetchedRecords.sort(
156+
(a, b) => (orderIndex.get(a.id) ?? 0) - (orderIndex.get(b.id) ?? 0),
156157
);
157-
const recordsToMerge = args.ids.map((id) => {
158-
const record = recordsById.get(id);
159-
160-
if (!record) {
161-
throw new CommonQueryRunnerException(
162-
`Record with id ${id} not found`,
163-
CommonQueryRunnerExceptionCode.RECORD_NOT_FOUND,
164-
{ userFriendlyMessage: msg`Record not found.` },
165-
);
166-
}
167158

168-
return record;
169-
});
159+
const recordsToMerge = fetchedRecords;
170160

171161
if (args.dryRun && args.selectedFieldsResult.relations) {
172162
await this.processNestedRelationsHelper.processNestedRelations({

packages/twenty-server/src/engine/core-modules/audit/utils/events/workspace-event/webhook/webhook-response.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const webhookResponseSchema = z.strictObject({
1111
url: z.string(),
1212
webhookId: z.string(),
1313
eventName: z.string(),
14+
error: z.string().optional(),
1415
}),
1516
});
1617

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Injectable } from '@nestjs/common';
2+
3+
import axios, { type AxiosInstance } from 'axios';
4+
5+
import { getSecureAdapter } from 'src/engine/core-modules/tool/utils/get-secure-axios-adapter.util';
6+
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
7+
8+
@Injectable()
9+
export class SecureHttpClientService {
10+
constructor(private readonly twentyConfigService: TwentyConfigService) {}
11+
12+
getHttpClient(): AxiosInstance {
13+
const isSafeModeEnabled = this.twentyConfigService.get(
14+
'OUTBOUND_HTTP_SAFE_MODE_ENABLED',
15+
);
16+
17+
return isSafeModeEnabled
18+
? axios.create({ adapter: getSecureAdapter() })
19+
: axios.create();
20+
}
21+
}

packages/twenty-server/src/engine/core-modules/tool/tool.module.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
55
import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity';
66
import { FileModule } from 'src/engine/core-modules/file/file.module';
77
import { JwtModule } from 'src/engine/core-modules/jwt/jwt.module';
8+
import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service';
89
import { CodeInterpreterTool } from 'src/engine/core-modules/tool/tools/code-interpreter-tool/code-interpreter-tool';
910
import { HttpTool } from 'src/engine/core-modules/tool/tools/http-tool/http-tool';
1011
import { SearchHelpCenterTool } from 'src/engine/core-modules/tool/tools/search-help-center-tool/search-help-center-tool';
@@ -24,7 +25,14 @@ import { MessagingImportManagerModule } from 'src/modules/messaging/message-impo
2425
SendEmailTool,
2526
SearchHelpCenterTool,
2627
CodeInterpreterTool,
28+
SecureHttpClientService,
29+
],
30+
exports: [
31+
HttpTool,
32+
SendEmailTool,
33+
SearchHelpCenterTool,
34+
CodeInterpreterTool,
35+
SecureHttpClientService,
2736
],
28-
exports: [HttpTool, SendEmailTool, SearchHelpCenterTool, CodeInterpreterTool],
2937
})
3038
export class ToolModule {}

packages/twenty-server/src/engine/core-modules/tool/tools/http-tool/http-tool.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import axios, { type AxiosRequestConfig } from 'axios';
44
import { isDefined } from 'twenty-shared/utils';
55
import { parseDataFromContentType } from 'twenty-shared/workflow';
66

7+
import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service';
78
import { HttpRequestInputZodSchema } from 'src/engine/core-modules/tool/tools/http-tool/http-tool.schema';
89
import { type HttpRequestInput } from 'src/engine/core-modules/tool/tools/http-tool/types/http-request-input.type';
910
import { type ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type';
@@ -12,16 +13,16 @@ import {
1213
type Tool,
1314
type ToolExecutionContext,
1415
} from 'src/engine/core-modules/tool/types/tool.type';
15-
import { getSecureAdapter } from 'src/engine/core-modules/tool/utils/get-secure-axios-adapter.util';
16-
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
1716

1817
@Injectable()
1918
export class HttpTool implements Tool {
2019
description =
2120
'Make an HTTP request to any URL with configurable method, headers, and body.';
2221
inputSchema = HttpRequestInputZodSchema;
2322

24-
constructor(private readonly twentyConfigService: TwentyConfigService) {}
23+
constructor(
24+
private readonly secureHttpClientService: SecureHttpClientService,
25+
) {}
2526

2627
async execute(
2728
parameters: ToolInput,
@@ -47,16 +48,7 @@ export class HttpTool implements Tool {
4748
}
4849
}
4950

50-
const isSafeModeEnabled = this.twentyConfigService.get(
51-
'HTTP_TOOL_SAFE_MODE_ENABLED',
52-
);
53-
54-
const axiosClient = isSafeModeEnabled
55-
? axios.create({
56-
adapter: getSecureAdapter(),
57-
})
58-
: axios.create();
59-
51+
const axiosClient = this.secureHttpClientService.getHttpClient();
6052
const response = await axiosClient(axiosConfig);
6153

6254
return {

0 commit comments

Comments
 (0)