feat: add default filter types (#2087)

* feat: add default filter types

* fix: fields doesn't need to be a function
This commit is contained in:
Jérémy M
2023-10-17 17:57:02 +02:00
committed by GitHub
parent 1549664416
commit c4fa36402b
23 changed files with 457 additions and 2 deletions

View File

@ -0,0 +1,22 @@
import {
GraphQLInputObjectType,
GraphQLList,
GraphQLNonNull,
GraphQLFloat,
} from 'graphql';
import { FilterIsEnumType } from './filter-is-enum-filter.type';
export const BigFloatFilterType = new GraphQLInputObjectType({
name: 'BigFloatType',
fields: {
eq: { type: GraphQLFloat },
gt: { type: GraphQLFloat },
gte: { type: GraphQLFloat },
in: { type: new GraphQLList(new GraphQLNonNull(GraphQLFloat)) },
lt: { type: GraphQLFloat },
lte: { type: GraphQLFloat },
neq: { type: GraphQLFloat },
is: { type: FilterIsEnumType },
},
});

View File

@ -0,0 +1,22 @@
import {
GraphQLInputObjectType,
GraphQLList,
GraphQLNonNull,
GraphQLInt,
} from 'graphql';
import { FilterIsEnumType } from './filter-is-enum-filter.type';
export const BigIntFilterType = new GraphQLInputObjectType({
name: 'BigIntFilter',
fields: {
eq: { type: GraphQLInt },
gt: { type: GraphQLInt },
gte: { type: GraphQLInt },
in: { type: new GraphQLList(new GraphQLNonNull(GraphQLInt)) },
lt: { type: GraphQLInt },
lte: { type: GraphQLInt },
neq: { type: GraphQLInt },
is: { type: FilterIsEnumType },
},
});

View File

@ -0,0 +1,19 @@
import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from 'graphql';
import { DateScalarType } from 'src/tenant/schema-builder/graphql-types/scalars/date.scalar';
import { FilterIsEnumType } from './filter-is-enum-filter.type';
export const DateFilterType = new GraphQLInputObjectType({
name: 'DateFilter',
fields: {
eq: { type: DateScalarType },
gt: { type: DateScalarType },
gte: { type: DateScalarType },
in: { type: new GraphQLList(new GraphQLNonNull(DateScalarType)) },
lt: { type: DateScalarType },
lte: { type: DateScalarType },
neq: { type: DateScalarType },
is: { type: FilterIsEnumType },
},
});

View File

@ -0,0 +1,19 @@
import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from 'graphql';
import { DatetimeScalarType } from 'src/tenant/schema-builder/graphql-types/scalars/datetime.scalar';
import { FilterIsEnumType } from './filter-is-enum-filter.type';
export const DatetimeFilterInputType = new GraphQLInputObjectType({
name: 'DatetimeFilter',
fields: {
eq: { type: DatetimeScalarType },
gt: { type: DatetimeScalarType },
gte: { type: DatetimeScalarType },
in: { type: new GraphQLList(new GraphQLNonNull(DatetimeScalarType)) },
lt: { type: DatetimeScalarType },
lte: { type: DatetimeScalarType },
neq: { type: DatetimeScalarType },
is: { type: FilterIsEnumType },
},
});

View File

@ -0,0 +1,9 @@
import { GraphQLEnumType } from 'graphql';
export const FilterIsEnumType = new GraphQLEnumType({
name: 'FilterIs',
values: {
PENDING: { value: 'PENDING' },
RELEASED: { value: 'RELEASED' },
},
});

View File

@ -0,0 +1,22 @@
import {
GraphQLInputObjectType,
GraphQLFloat,
GraphQLList,
GraphQLNonNull,
} from 'graphql';
import { FilterIsEnumType } from './filter-is-enum-filter.type';
export const FloatFilter = new GraphQLInputObjectType({
name: 'FloatFilter',
fields: {
eq: { type: GraphQLFloat },
gt: { type: GraphQLFloat },
gte: { type: GraphQLFloat },
in: { type: new GraphQLList(new GraphQLNonNull(GraphQLFloat)) },
lt: { type: GraphQLFloat },
lte: { type: GraphQLFloat },
neq: { type: GraphQLFloat },
is: { type: FilterIsEnumType },
},
});

View File

@ -0,0 +1,22 @@
import {
GraphQLInputObjectType,
GraphQLList,
GraphQLNonNull,
GraphQLInt,
} from 'graphql';
import { FilterIsEnumType } from './filter-is-enum-filter.type';
export const IntFilter = new GraphQLInputObjectType({
name: 'IntFilter',
fields: {
eq: { type: GraphQLInt },
gt: { type: GraphQLInt },
gte: { type: GraphQLInt },
in: { type: new GraphQLList(new GraphQLNonNull(GraphQLInt)) },
lt: { type: GraphQLInt },
lte: { type: GraphQLInt },
neq: { type: GraphQLInt },
is: { type: FilterIsEnumType },
},
});

View File

@ -0,0 +1,12 @@
import { GraphQLInputObjectType } from 'graphql';
import { StringFilterType } from 'src/tenant/schema-builder/graphql-types/input/string-filter.type';
import { IntFilter } from 'src/tenant/schema-builder/graphql-types/input/int-filter.type';
export const MoneyFilterType = new GraphQLInputObjectType({
name: 'MoneyFilter',
fields: {
amount: { type: IntFilter },
currency: { type: StringFilterType },
},
});

View File

@ -0,0 +1,27 @@
import {
GraphQLInputObjectType,
GraphQLList,
GraphQLNonNull,
GraphQLString,
} from 'graphql';
import { FilterIsEnumType } from './filter-is-enum-filter.type';
export const StringFilterType = new GraphQLInputObjectType({
name: 'StringFilter',
fields: {
eq: { type: GraphQLString },
gt: { type: GraphQLString },
gte: { type: GraphQLString },
in: { type: new GraphQLList(new GraphQLNonNull(GraphQLString)) },
lt: { type: GraphQLString },
lte: { type: GraphQLString },
neq: { type: GraphQLString },
is: { type: FilterIsEnumType },
startsWith: { type: GraphQLString },
like: { type: GraphQLString },
ilike: { type: GraphQLString },
regex: { type: GraphQLString },
iregex: { type: GraphQLString },
},
});

View File

@ -0,0 +1,19 @@
import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from 'graphql';
import { TimeScalarType } from 'src/tenant/schema-builder/graphql-types/scalars/time.scalar';
import { FilterIsEnumType } from './filter-is-enum-filter.type';
export const TimeFilter = new GraphQLInputObjectType({
name: 'TimeFilter',
fields: {
eq: { type: TimeScalarType },
gt: { type: TimeScalarType },
gte: { type: TimeScalarType },
in: { type: new GraphQLList(new GraphQLNonNull(TimeScalarType)) },
lt: { type: TimeScalarType },
lte: { type: TimeScalarType },
neq: { type: TimeScalarType },
is: { type: FilterIsEnumType },
},
});

View File

@ -0,0 +1,11 @@
import { GraphQLInputObjectType } from 'graphql';
import { StringFilterType } from 'src/tenant/schema-builder/graphql-types/input/string-filter.type';
export const UrlFilterType = new GraphQLInputObjectType({
name: 'UrlFilter',
fields: {
text: { type: StringFilterType },
link: { type: StringFilterType },
},
});

View File

@ -0,0 +1,15 @@
import { GraphQLInputObjectType, GraphQLList } from 'graphql';
import { UUIDScalarType } from 'src/tenant/schema-builder/graphql-types/scalars/uuid.scalar';
import { FilterIsEnumType } from './filter-is-enum-filter.type';
export const UUIDFilterType = new GraphQLInputObjectType({
name: 'UUIDFilter',
fields: {
eq: { type: UUIDScalarType },
in: { type: new GraphQLList(UUIDScalarType) },
neq: { type: UUIDScalarType },
is: { type: FilterIsEnumType },
},
});

View File

@ -0,0 +1,21 @@
import { GraphQLScalarType } from 'graphql';
import { Kind } from 'graphql/language';
export const BigFloatScalarType = new GraphQLScalarType({
name: 'BigFloat',
description:
'A custom scalar type for representing big floating point numbers',
serialize(value: number): string {
return String(value);
},
parseValue(value: string): number {
return parseFloat(value);
},
parseLiteral(ast): number | null {
if (ast.kind === Kind.FLOAT) {
return parseFloat(ast.value);
}
return null;
},
});

View File

@ -0,0 +1,20 @@
import { GraphQLScalarType } from 'graphql';
export const BigIntScalarType = new GraphQLScalarType({
name: 'BigInt',
description:
'The `BigInt` scalar type represents non-fractional signed whole numeric values.',
serialize(value: bigint): string {
return value.toString();
},
parseValue(value: string): bigint {
return BigInt(value);
},
parseLiteral(ast): bigint | null {
if (ast.kind === 'IntValue') {
return BigInt(ast.value);
}
return null;
},
});

View File

@ -0,0 +1,20 @@
import { GraphQLScalarType } from 'graphql';
import { Kind } from 'graphql/language';
export const DateScalarType = new GraphQLScalarType({
name: 'Date',
description: 'Date custom scalar type',
serialize(value: Date): number {
return value.getTime();
},
parseValue(value: number): Date {
return new Date(value);
},
parseLiteral(ast): Date | null {
if (ast.kind === Kind.INT) {
return new Date(parseInt(ast.value, 10));
}
return null;
},
});

View File

@ -0,0 +1,38 @@
import { GraphQLScalarType } from 'graphql';
import { Kind } from 'graphql/language';
export const DatetimeScalarType = new GraphQLScalarType({
name: 'Datetime',
description: 'A custom scalar that represents a datetime in ISO format',
serialize(value: string): string {
const date = new Date(value);
if (isNaN(date.getTime())) {
throw new Error('Invalid date format, expected ISO date string');
}
return date.toISOString();
},
parseValue(value: string): Date {
const date = new Date(value);
if (isNaN(date.getTime())) {
throw new Error('Invalid date format, expected ISO date string');
}
return date;
},
parseLiteral(ast): Date {
if (ast.kind !== Kind.STRING) {
throw new Error('Invalid date format, expected ISO date string');
}
const date = new Date(ast.value);
if (isNaN(date.getTime())) {
throw new Error('Invalid date format, expected ISO date string');
}
return date;
},
});

View File

@ -0,0 +1,24 @@
import { GraphQLScalarType } from 'graphql';
import { IntValueNode, Kind } from 'graphql/language';
export const TimeScalarType = new GraphQLScalarType({
name: 'Time',
description: 'Time custom scalar type',
serialize(value: Date): number {
return value.getTime();
},
parseValue(value: number): Date {
return new Date(value);
},
parseLiteral(ast): Date {
if (ast.kind === Kind.INT) {
const intAst = ast as IntValueNode;
if (typeof intAst.value === 'number') {
return new Date(intAst.value);
}
throw new Error(`Invalid timestamp value: ${ast.value}`);
}
throw new Error(`Invalid AST kind: ${ast.kind}`);
},
});

View File

@ -0,0 +1,27 @@
import { GraphQLScalarType, Kind } from 'graphql';
export const UUIDScalarType = new GraphQLScalarType({
name: 'UUID',
description: 'A UUID scalar type',
serialize(value: string): string {
if (typeof value !== 'string') {
throw new Error('UUID must be a string');
}
return value;
},
parseValue(value: string): string {
if (typeof value !== 'string') {
throw new Error('UUID must be a string');
}
return value;
},
parseLiteral(ast): string {
if (ast.kind !== Kind.STRING) {
throw new Error('UUID must be a string');
}
return ast.value;
},
});

View File

@ -5,7 +5,7 @@ import {
GraphQLString,
} from 'graphql';
import { PageInfoType } from 'src/tenant/schema-builder/utils/page-into-type.util';
import { PageInfoType } from 'src/tenant/schema-builder/graphql-types/object/page-into.type';
import { generateConnectionType } from 'src/tenant/schema-builder/utils/generate-connection-type.util';
describe('generateConnectionType', () => {

View File

@ -0,0 +1,47 @@
import { GraphQLBoolean } from 'graphql';
import { mapColumnTypeToFilterType } from 'src/tenant/schema-builder/utils/map-column-type-to-filter-type.util'; // Update with the actual path
import { FieldMetadata } from 'src/metadata/field-metadata/field-metadata.entity';
import { UUIDFilterType } from 'src/tenant/schema-builder/graphql-types/input/uuid-filter.type';
import { StringFilterType } from 'src/tenant/schema-builder/graphql-types/input/string-filter.type';
import { DateFilterType } from 'src/tenant/schema-builder/graphql-types/input/date-filter.type';
import { IntFilter } from 'src/tenant/schema-builder/graphql-types/input/int-filter.type';
import { UrlFilterType } from 'src/tenant/schema-builder/graphql-types/input/url-filter.type';
import { MoneyFilterType } from 'src/tenant/schema-builder/graphql-types/input/money-filter.type';
describe('mapColumnTypeToFilterType', () => {
it('should map column types to corresponding filter types', () => {
const fields: { column: FieldMetadata; expected: any }[] = [
{ column: { type: 'uuid' } as FieldMetadata, expected: UUIDFilterType },
{ column: { type: 'text' } as FieldMetadata, expected: StringFilterType },
{
column: { type: 'phone' } as FieldMetadata,
expected: StringFilterType,
},
{
column: { type: 'email' } as FieldMetadata,
expected: StringFilterType,
},
{ column: { type: 'date' } as FieldMetadata, expected: DateFilterType },
{
column: { type: 'boolean' } as FieldMetadata,
expected: GraphQLBoolean,
},
{ column: { type: 'number' } as FieldMetadata, expected: IntFilter },
{ column: { type: 'url' } as FieldMetadata, expected: UrlFilterType },
{ column: { type: 'money' } as FieldMetadata, expected: MoneyFilterType },
];
fields.forEach((field) => {
expect(mapColumnTypeToFilterType(field.column)).toBe(field.expected);
});
});
it('should throw an error for unimplemented filter types', () => {
const column = { type: 'enum' } as FieldMetadata;
expect(() => mapColumnTypeToFilterType(column)).toThrowError(
'enum filter type not yet implemented',
);
});
});

View File

@ -1,6 +1,6 @@
import { GraphQLList, GraphQLNonNull, GraphQLObjectType } from 'graphql';
import { PageInfoType } from './page-into-type.util';
import { PageInfoType } from 'src/tenant/schema-builder/graphql-types/object/page-into.type';
/**
* Generate a GraphQL connection type based on the EdgeType.

View File

@ -0,0 +1,39 @@
import { GraphQLBoolean } from 'graphql';
import { FieldMetadata } from 'src/metadata/field-metadata/field-metadata.entity';
import { UUIDFilterType } from 'src/tenant/schema-builder/graphql-types/input/uuid-filter.type';
import { StringFilterType } from 'src/tenant/schema-builder/graphql-types/input/string-filter.type';
import { DateFilterType } from 'src/tenant/schema-builder/graphql-types/input/date-filter.type';
import { IntFilter } from 'src/tenant/schema-builder/graphql-types/input/int-filter.type';
import { UrlFilterType } from 'src/tenant/schema-builder/graphql-types/input/url-filter.type';
import { MoneyFilterType } from 'src/tenant/schema-builder/graphql-types/input/money-filter.type';
/**
* Map the column type from field-metadata to its corresponding filter type.
* @param columnType Type of the column in the database.
*/
export const mapColumnTypeToFilterType = (column: FieldMetadata) => {
switch (column.type) {
case 'uuid':
return UUIDFilterType;
case 'text':
case 'phone':
case 'email':
return StringFilterType;
case 'date':
return DateFilterType;
case 'boolean':
return GraphQLBoolean;
case 'number':
return IntFilter;
case 'url': {
return UrlFilterType;
}
case 'money': {
return MoneyFilterType;
}
case 'enum':
default:
throw new Error(`${column.type} filter type not yet implemented`);
}
};