Marie
2025-11-27 11:22:57 +01:00
committed by GitHub
parent 7e90dd888c
commit 9c9a01d55a
25 changed files with 437 additions and 42 deletions
@@ -1,5 +1,7 @@
import { type ObjectRecordGroupByDateGranularity } from 'twenty-shared/types';
import { type FirstDayOfTheWeek } from 'twenty-shared/utils';
export type DateFieldGroupByDefinition = {
granularity: ObjectRecordGroupByDateGranularity;
weekStartDay?: FirstDayOfTheWeek;
};
@@ -1,4 +1,5 @@
import { type ObjectRecordGroupByDateGranularity } from 'twenty-shared/types';
import { type FirstDayOfTheWeek } from 'twenty-shared/utils';
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
@@ -10,12 +11,14 @@ export type GroupByDateField = {
fieldMetadata: FieldMetadataEntity;
subFieldName?: string;
dateGranularity: ObjectRecordGroupByDateGranularity;
weekStartDay?: FirstDayOfTheWeek;
};
export type GroupByRelationField = {
fieldMetadata: FieldMetadataEntity;
nestedFieldMetadata: FieldMetadataEntity;
nestedSubFieldName?: string;
dateGranularity?: ObjectRecordGroupByDateGranularity;
weekStartDay?: FirstDayOfTheWeek;
};
export type GroupByField =
| GroupByRegularField
@@ -33,11 +33,25 @@ export const getGroupByExpression = ({
return `TRIM(TO_CHAR(${columnNameWithQuotes}, 'TMMonth'))`;
case ObjectRecordGroupByDateGranularity.QUARTER_OF_THE_YEAR:
return `TRIM(TO_CHAR(${columnNameWithQuotes}, '"Q"Q'))`;
case ObjectRecordGroupByDateGranularity.WEEK: {
const weekStartDay = groupByField.weekStartDay;
let shiftedExpression = `DATE_TRUNC('week', ${columnNameWithQuotes})`;
if (isDefined(weekStartDay)) {
if (weekStartDay === 'SUNDAY') {
shiftedExpression = `DATE_TRUNC('week', ${columnNameWithQuotes} + INTERVAL '1 day') - INTERVAL '1 day'`;
} else if (weekStartDay === 'SATURDAY') {
shiftedExpression = `DATE_TRUNC('week', ${columnNameWithQuotes} + INTERVAL '2 days') - INTERVAL '2 days'`;
}
}
return `TO_CHAR(${shiftedExpression}, 'YYYY-MM-DD"T"HH24:MI:SSTZH:TZM')`;
}
case ObjectRecordGroupByDateGranularity.DAY:
case ObjectRecordGroupByDateGranularity.MONTH:
case ObjectRecordGroupByDateGranularity.QUARTER:
case ObjectRecordGroupByDateGranularity.YEAR:
return `DATE_TRUNC('${dateGranularity}', ${columnNameWithQuotes})`;
return `TO_CHAR(DATE_TRUNC('${dateGranularity}', ${columnNameWithQuotes}), 'YYYY-MM-DD"T"HH24:MI:SSTZH:TZM')`;
default:
assertUnreachable(dateGranularity);
}
@@ -72,6 +72,7 @@ export const parseGroupByArgs = (
groupByFields.push({
fieldMetadata,
dateGranularity: fieldGroupByDefinition.granularity,
weekStartDay: fieldGroupByDefinition.weekStartDay,
});
continue;
}
@@ -1,9 +1,6 @@
import { UserInputError } from 'apollo-server-core';
import { isDefined } from 'class-validator';
import {
FieldMetadataType,
type ObjectRecordGroupByDateGranularity,
} from 'twenty-shared/types';
import { FieldMetadataType } from 'twenty-shared/types';
import {
GraphqlQueryRunnerException,
@@ -153,14 +150,14 @@ export const parseGroupByRelationField = ({
nestedFieldMetadata.type === FieldMetadataType.DATE_TIME) &&
isGroupByDateFieldDefinition(nestedFieldGroupByDefinition)
) {
const dateFieldDefinition =
nestedFieldGroupByDefinition as DateFieldGroupByDefinition;
groupByFields.push({
fieldMetadata,
nestedFieldMetadata,
dateGranularity: (
nestedFieldGroupByDefinition as {
granularity: ObjectRecordGroupByDateGranularity;
}
).granularity,
dateGranularity: dateFieldDefinition.granularity,
weekStartDay: dateFieldDefinition.weekStartDay,
});
return;
@@ -4,6 +4,7 @@ import {
type ObjectRecordOrderByForCompositeField,
type ObjectRecordOrderByForScalarField,
} from 'twenty-shared/types';
import { type FirstDayOfTheWeek } from 'twenty-shared/utils';
export type ObjectRecordFilter = Partial<{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -27,6 +28,7 @@ export type ObjectRecordGroupByForCompositeField = Partial<{
export type ObjectRecordGroupByForDateField = Partial<{
[Property in keyof ObjectRecord]: {
granularity: ObjectRecordGroupByDateGranularity;
weekStartDay?: FirstDayOfTheWeek;
};
}>;
@@ -1,7 +1,10 @@
import { Injectable } from '@nestjs/common';
import { GraphQLEnumType, GraphQLInputObjectType } from 'graphql';
import { ObjectRecordGroupByDateGranularity } from 'twenty-shared/types';
import {
FirstDayOfTheWeek,
ObjectRecordGroupByDateGranularity,
} from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { OrderByDirectionType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/enum';
@@ -30,7 +33,7 @@ export class GroupByDateGranularityInputTypeGenerator {
{} as Record<string, { value: string }>,
),
description:
'Date granularity (e.g. day, month, quarter, year, day of the week, quarter of the year, month of the year)',
'Date granularity (e.g. day, month, quarter, year, week, day of the week, quarter of the year, month of the year)',
}),
);
@@ -42,13 +45,33 @@ export class GroupByDateGranularityInputTypeGenerator {
throw new Error('DateGranularityEnum not found');
}
const firstDayOfWeekEnum = new GraphQLEnumType({
name: 'FirstDayOfTheWeek',
values: Object.values(FirstDayOfTheWeek).reduce(
(acc, option) => {
acc[option] = { value: option };
return acc;
},
{} as Record<string, { value: string }>,
),
description: 'First day of the week (MONDAY, SUNDAY, SATURDAY)',
});
this.gqlTypesStorage.addGqlType('FirstDayOfTheWeek', firstDayOfWeekEnum);
const groupByDateField = new GraphQLInputObjectType({
name: GROUP_BY_DATE_GRANULARITY_INPUT_KEY,
fields: {
granularity: {
type: dateGranularityEnum,
description:
'Date granularity (e.g. day, month, quarter, year, day of the week, quarter of the year, month of the year)',
'Date granularity (e.g. day, month, quarter, year, week, day of the week, quarter of the year, month of the year)',
},
weekStartDay: {
type: firstDayOfWeekEnum,
description:
'First day of the week (only applicable when granularity is WEEK). Defaults to MONDAY if not specified.',
},
},
});