fix: regression: sort day by fileCreatedAt again (#18732)

* fix: regression: sort day by fileCreatedAt again

* lint

* e2e test

* inline function

* e2e

* Address comments. Drop dayGroup and timezone in favor of localOffsetMinutes

* lint and some api-doc

* lint, more api-doc

* format

* Move minutes to fractional hours

* make sql

* merge/conflict

* merge fallout, review comments

* spelling

* drop offset from returned date

* move description into decorator where possible, regen api
This commit is contained in:
Min Idzelis
2025-06-05 21:56:32 -04:00
committed by GitHub
parent 81423420c8
commit 55f4e93456
23 changed files with 687 additions and 247 deletions

View File

@@ -6,7 +6,7 @@ import {
ParseUUIDPipe,
applyDecorators,
} from '@nestjs/common';
import { ApiProperty } from '@nestjs/swagger';
import { ApiProperty, ApiPropertyOptions } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
import {
IsArray,
@@ -72,22 +72,28 @@ export class UUIDParamDto {
}
type PinCodeOptions = { optional?: boolean } & OptionalOptions;
export const PinCode = ({ optional, ...options }: PinCodeOptions = {}) => {
export const PinCode = (options?: PinCodeOptions & ApiPropertyOptions) => {
const { optional, nullable, emptyToNull, ...apiPropertyOptions } = {
optional: false,
nullable: false,
emptyToNull: false,
...options,
};
const decorators = [
IsString(),
IsNotEmpty(),
Matches(/^\d{6}$/, { message: ({ property }) => `${property} must be a 6-digit numeric string` }),
ApiProperty({ example: '123456' }),
ApiProperty({ example: '123456', ...apiPropertyOptions }),
];
if (optional) {
decorators.push(Optional(options));
decorators.push(Optional({ nullable, emptyToNull }));
}
return applyDecorators(...decorators);
};
export interface OptionalOptions extends ValidationOptions {
export interface OptionalOptions {
nullable?: boolean;
/** convert empty strings to null */
emptyToNull?: boolean;
@@ -127,22 +133,32 @@ export const ValidateHexColor = () => {
};
type UUIDOptions = { optional?: boolean; each?: boolean; nullable?: boolean };
export const ValidateUUID = (options?: UUIDOptions) => {
const { optional, each, nullable } = { optional: false, each: false, nullable: false, ...options };
export const ValidateUUID = (options?: UUIDOptions & ApiPropertyOptions) => {
const { optional, each, nullable, ...apiPropertyOptions } = {
optional: false,
each: false,
nullable: false,
...options,
};
return applyDecorators(
IsUUID('4', { each }),
ApiProperty({ format: 'uuid' }),
ApiProperty({ format: 'uuid', ...apiPropertyOptions }),
optional ? Optional({ nullable }) : IsNotEmpty(),
each ? IsArray() : IsString(),
);
};
type DateOptions = { optional?: boolean; nullable?: boolean; format?: 'date' | 'date-time' };
export const ValidateDate = (options?: DateOptions) => {
const { optional, nullable, format } = { optional: false, nullable: false, format: 'date-time', ...options };
export const ValidateDate = (options?: DateOptions & ApiPropertyOptions) => {
const { optional, nullable, format, ...apiPropertyOptions } = {
optional: false,
nullable: false,
format: 'date-time',
...options,
};
const decorators = [
ApiProperty({ format }),
ApiProperty({ format, ...apiPropertyOptions }),
IsDate(),
optional ? Optional({ nullable: true }) : IsNotEmpty(),
Transform(({ key, value }) => {
@@ -166,9 +182,12 @@ export const ValidateDate = (options?: DateOptions) => {
};
type AssetVisibilityOptions = { optional?: boolean };
export const ValidateAssetVisibility = (options?: AssetVisibilityOptions) => {
const { optional } = { optional: false, ...options };
const decorators = [IsEnum(AssetVisibility), ApiProperty({ enumName: 'AssetVisibility', enum: AssetVisibility })];
export const ValidateAssetVisibility = (options?: AssetVisibilityOptions & ApiPropertyOptions) => {
const { optional, ...apiPropertyOptions } = { optional: false, ...options };
const decorators = [
IsEnum(AssetVisibility),
ApiProperty({ enumName: 'AssetVisibility', enum: AssetVisibility, ...apiPropertyOptions }),
];
if (optional) {
decorators.push(Optional());
@@ -177,10 +196,10 @@ export const ValidateAssetVisibility = (options?: AssetVisibilityOptions) => {
};
type BooleanOptions = { optional?: boolean };
export const ValidateBoolean = (options?: BooleanOptions) => {
const { optional } = { optional: false, ...options };
export const ValidateBoolean = (options?: BooleanOptions & ApiPropertyOptions) => {
const { optional, ...apiPropertyOptions } = { optional: false, ...options };
const decorators = [
// ApiProperty(),
ApiProperty(apiPropertyOptions),
IsBoolean(),
Transform(({ value }) => {
if (value == 'true') {