Common api - chores (#17051)

Remove refacto-common TODO

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Etienne
2026-01-12 12:37:29 +01:00
committed by GitHub
parent 7735a0fc7d
commit 238e6d5cda
16 changed files with 329 additions and 149 deletions
@@ -27,6 +27,7 @@ import {
import {
computeBatchPath,
computeDuplicatesResultPath,
computeGroupByResultPath,
computeManyResultPath,
computeMergeManyResultPath,
computeRestoreManyResultPath,
@@ -161,6 +162,11 @@ export class OpenApiService {
flatObjectMetadataMaps,
flatFieldMetadataMaps,
);
paths[`/${item.namePlural}/groupBy`] = computeGroupByResultPath(
item,
flatObjectMetadataMaps,
flatFieldMetadataMaps,
);
return paths;
}, schema.paths as OpenAPIV3_1.PathsObject);
@@ -60,8 +60,8 @@ describe('computeParameters', () => {
expect(computeDepthParameters()).toEqual({
name: 'depth',
in: 'query',
description: `Determines the level of nested related objects to include in the response.
- 0: Primary object only
description: `Determines the level of nested related objects to include in the response.
- 0: Primary object only
- 1: Primary object + direct relations`,
required: false,
schema: {
@@ -77,7 +77,7 @@ describe('computeParameters', () => {
expect(computeFilterParameters()).toEqual({
name: 'filter',
in: 'query',
description: `Format: field[COMPARATOR]:value,field2[COMPARATOR]:value2
description: `Format: field[COMPARATOR]:value,field2[COMPARATOR]:value2
Refer to the filter section at the top of the page for more details.`,
required: false,
schema: {
@@ -7,15 +7,20 @@ import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfa
import { generateRandomFieldValue } from 'src/engine/core-modules/open-api/utils/generate-random-field-value.util';
import {
computeAggregateParameters,
computeDepthParameters,
computeEndingBeforeParameters,
computeFilterParameters,
computeGroupByParameters,
computeIdPathParameter,
computeIncludeRecordsSampleParameters,
computeLimitParameters,
computeOrderByForRecordsParameters,
computeOrderByParameters,
computeSoftDeleteParameters,
computeStartingAfterParameters,
computeUpsertParameters,
computeViewIdParameters,
} from 'src/engine/core-modules/open-api/utils/parameters.utils';
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
@@ -277,6 +282,11 @@ export const computeParameterComponents = (
softDelete: computeSoftDeleteParameters(),
orderBy: computeOrderByParameters(),
limit: computeLimitParameters(fromMetadata),
groupBy: computeGroupByParameters(),
viewId: computeViewIdParameters(),
aggregate: computeAggregateParameters(),
includeRecordsSample: computeIncludeRecordsSampleParameters(),
orderByForRecords: computeOrderByForRecordsParameters(),
};
};
@@ -49,8 +49,8 @@ export const computeDepthParameters = (): OpenAPIV3_1.ParameterObject => {
return {
name: 'depth',
in: 'query',
description: `Determines the level of nested related objects to include in the response.
- 0: Primary object only
description: `Determines the level of nested related objects to include in the response.
- 0: Primary object only
- 1: Primary object + direct relations`,
required: false,
schema: {
@@ -93,7 +93,7 @@ export const computeFilterParameters = (): OpenAPIV3_1.ParameterObject => {
return {
name: 'filter',
in: 'query',
description: `Format: field[COMPARATOR]:value,field2[COMPARATOR]:value2
description: `Format: field[COMPARATOR]:value,field2[COMPARATOR]:value2
Refer to the filter section at the top of the page for more details.`,
required: false,
schema: {
@@ -157,3 +157,98 @@ export const computeIdPathParameter = (): OpenAPIV3_1.ParameterObject => {
},
};
};
export const computeGroupByParameters = (): OpenAPIV3_1.ParameterObject => {
return {
name: 'group_by',
in: 'query',
description: `Array of fields to group by. Each element can specify a field and optionally a subfield or granularity for date fields.`,
required: true,
schema: {
type: 'string',
},
examples: {
simple: {
value: '[{"updatedAt": true}]',
summary: 'Group by a single field',
},
subfield: {
value: '[{"assignee": {"name": true}}]',
summary: 'Group by a relation field subfield',
},
dateGranularity: {
value: '[{"createdAt": {"granularity": "MONTH"}}]',
summary: 'Group by date with granularity (DAY, WEEK, MONTH, YEAR)',
},
},
};
};
export const computeViewIdParameters = (): OpenAPIV3_1.ParameterObject => {
return {
name: 'view_id',
in: 'query',
description: 'View ID to apply filters from.',
required: false,
schema: {
type: 'string',
format: 'uuid',
},
};
};
export const computeIncludeRecordsSampleParameters =
(): OpenAPIV3_1.ParameterObject => {
return {
name: 'include_records_sample',
in: 'query',
description:
'If true, includes a sample of records for each group in the response.',
required: false,
schema: {
type: 'boolean',
default: false,
},
};
};
export const computeAggregateParameters = (): OpenAPIV3_1.ParameterObject => {
return {
name: 'aggregate',
in: 'query',
description: `Array of aggregate operations to compute for each group.`,
required: false,
schema: {
type: 'string',
},
examples: {
count: {
value: '["countNotEmptyId"]',
summary: 'Count non-empty IDs in each group',
},
multiple: {
value: '["countNotEmptyId", "sumAmount"]',
summary: 'Multiple aggregate operations',
},
},
};
};
export const computeOrderByForRecordsParameters =
(): OpenAPIV3_1.ParameterObject => {
return {
name: 'order_by_for_records',
in: 'query',
description: `Order by clause for records within each group. Only applicable when include_records_sample is true.`,
required: false,
schema: {
type: 'string',
},
examples: {
simple: {
value: 'createdAt',
summary: 'Order records by createdAt',
},
},
};
};
@@ -16,6 +16,7 @@ import {
getFindDuplicatesResponse200,
getFindManyResponse200,
getFindOneResponse200,
getGroupByResponse200,
getJsonResponse,
getMergeManyResponse200,
getRestoreManyResponse200,
@@ -325,3 +326,37 @@ export const computeMergeManyResultPath = (
},
} as OpenAPIV3_1.PathItemObject;
};
export const computeGroupByResultPath = (
item: Pick<FlatObjectMetadata, 'nameSingular' | 'namePlural'>,
_flatObjectMetadataMaps: Pick<
AllFlatEntityMaps,
'flatObjectMetadataMaps'
>['flatObjectMetadataMaps'],
_flatFieldMetadataMaps: Pick<
AllFlatEntityMaps,
'flatFieldMetadataMaps'
>['flatFieldMetadataMaps'],
): OpenAPIV3_1.PathItemObject => {
return {
get: {
tags: [item.namePlural],
summary: `Group By ${item.namePlural}`,
description: `Groups **${item.namePlural}** by specified fields and optionally computes aggregate values for each group.`,
operationId: `groupBy${capitalize(item.namePlural)}`,
parameters: [
{ $ref: '#/components/parameters/groupBy' },
{ $ref: '#/components/parameters/filter' },
{ $ref: '#/components/parameters/orderBy' },
{ $ref: '#/components/parameters/limit' },
{ $ref: '#/components/parameters/viewId' },
{ $ref: '#/components/parameters/aggregate' },
{ $ref: '#/components/parameters/includeRecordsSample' },
{ $ref: '#/components/parameters/orderByForRecords' },
],
responses: {
'200': getGroupByResponse200(item),
},
},
} as OpenAPIV3_1.PathItemObject;
};
@@ -468,3 +468,57 @@ export const getMergeManyResponse200 = (
},
};
};
export const getGroupByResponse200 = (
item: Pick<FlatObjectMetadata, 'nameSingular' | 'namePlural'>,
) => {
const schemaRef = `#/components/schemas/${capitalize(
item.nameSingular,
)}ForResponse`;
return {
description: 'Successful operation',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
data: {
type: 'object',
properties: {
[`${item.namePlural}GroupBy`]: {
type: 'array',
items: {
type: 'object',
properties: {
groupByDimensionValues: {
type: 'array',
description:
'Array of values representing each dimension in the group',
items: {
type: 'string',
},
},
records: {
type: 'array',
description:
'Sample of records for this group (only present when include_records_sample is true)',
items: {
$ref: schemaRef,
},
},
},
additionalProperties: {
type: 'number',
description: 'Aggregate values (e.g., countNotEmptyId)',
},
},
},
},
},
},
},
},
},
};
};