Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: record filter query e2e #324

Merged
merged 12 commits into from
Jan 10, 2024
8 changes: 5 additions & 3 deletions .github/workflows/docker-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ jobs:
uses: docker/setup-buildx-action@v3

- name: Login to Alibaba Cloud Docker Registry
run: docker login --username=${{ secrets.ALI_DOCKER_USERNAME }} registry.cn-beijing.aliyuncs.com --password=${{ secrets.ALI_DOCKER_PASSWORD }}
run: |
docker login --username=${{ secrets.ALI_DOCKER_USERNAME }} registry.cn-beijing.aliyuncs.com --password=${{ secrets.ALI_DOCKER_PASSWORD }}
docker login --username=${{ secrets.DOCKER_USERNAME }} --password=${{ secrets.DOCKER_PASSWORD }}

- name: Build and push Docker image
run: |
# --platform linux/amd64,linux/arm64
docker buildx build \
--cache-from type=registry,ref=registry.cn-beijing.aliyuncs.com/bieber/teable:build-cache \
--cache-to type=registry,ref=registry.cn-beijing.aliyuncs.com/bieber/teable:build-cache,mode=max \
--cache-from type=registry,ref=docker.io/teablecloud/teable:build-cache \
--cache-to type=registry,ref=docker.io/teablecloud/teable:build-cache,mode=max \
--file dockers/teable/Dockerfile \
--tag registry.cn-beijing.aliyuncs.com/bieber/teable:latest \
--tag registry.cn-beijing.aliyuncs.com/bieber/teable:${GITHUB_SHA} \
Expand Down
7 changes: 4 additions & 3 deletions apps/nestjs-backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"test": "run-s test-unit test-e2e",
"test-unit": "vitest run --silent --bail 1",
"test-cov": "vitest run --coverage",
"test-e2e": "vitest run --config ./vitest-e2e.config.ts --silent --bail 1",
"pre-test-e2e": "cross-env NODE_ENV=test pnpm -F @teable-group/db-main-prisma prisma-db-seed -- --e2e",
"test-e2e": "pnpm pre-test-e2e && vitest run --config ./vitest-e2e.config.ts --silent --bail 1",
"typecheck": "tsc --project ./tsconfig.json --noEmit",
"lint": "eslint . --ext .ts,.js,.cjs,.mjs,.mdx --cache --cache-location ../../.cache/eslint/nestjs-backend.eslintcache",
"fix-all-files": "eslint . --ext .ts,.tsx,.js,.jsx,.cjs,.mjs,.mdx --fix",
Expand Down Expand Up @@ -116,8 +117,8 @@
"@opentelemetry/resources": "1.19.0",
"@opentelemetry/sdk-node": "0.46.0",
"@opentelemetry/semantic-conventions": "1.19.0",
"@prisma/client": "5.7.1",
"@prisma/instrumentation": "5.7.1",
"@prisma/client": "5.8.0",
"@prisma/instrumentation": "5.8.0",
"@teable-group/common-i18n": "workspace:^",
"@teable-group/core": "workspace:^",
"@teable-group/db-main-prisma": "workspace:^",
Expand Down
8 changes: 7 additions & 1 deletion apps/nestjs-backend/src/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,14 @@ export async function bootstrap() {

const port = await getAvailablePort(configService.get<string>('PORT') as string);
process.env.PORT = port.toString();
logger.log(`> Ready on http://${host}:${port}`);

const now = new Date();
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
logger.log(`> NODE_ENV is ${process.env.NODE_ENV}`);
logger.log(`> Ready on http://${host}:${port}`);
logger.log('> System Time Zone:', timeZone);
logger.log('> Current System Time:', now.toString());

await app.listen(port);
return app;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
import type {
IDateFieldOptions,
IDateFilter,
IFilter,
IFilterOperator,
IFilterValue,
} from '@teable-group/core';
Expand Down Expand Up @@ -39,29 +38,24 @@ import {
} from '@teable-group/core';
import type { Dayjs } from 'dayjs';
import type { Knex } from 'knex';
import { get } from 'lodash';
import type { IFieldInstance } from '../../features/field/model/factory';
import type { ICellValueFilterInterface } from './cell-value-filter.interface';

export abstract class AbstractCellValueFilter implements ICellValueFilterInterface {
protected _table: string;
protected tableColumnRef: string;
protected columnName: string;

constructor(
protected readonly queryBuilder: Knex.QueryBuilder,
protected readonly fields?: { [fieldId: string]: IFieldInstance },
protected readonly filter?: IFilter | null
protected readonly dbTableName: string,
protected readonly field: IFieldInstance
) {
this._table = get(queryBuilder, ['_single', 'table']);
const { dbFieldName } = this.field;

this.columnName = dbFieldName;
this.tableColumnRef = `${this.dbTableName}.${dbFieldName}`;
}

filterStrategies(
operator: IFilterOperator,
params: {
queryBuilder: Knex.QueryBuilder;
field: IFieldInstance;
value: IFilterValue;
}
) {
compiler(builderClient: Knex.QueryBuilder, operator: IFilterOperator, value: IFilterValue) {
const operatorHandlers = {
[is.value]: this.isOperatorHandler,
[isExactly.value]: this.isExactlyOperatorHandler,
Expand Down Expand Up @@ -91,140 +85,146 @@ export abstract class AbstractCellValueFilter implements ICellValueFilterInterfa
throw new InternalServerErrorException(`Unknown operator ${operator} for filter`);
}

const { field, value } = params;
return chosenHandler(params.queryBuilder, { field, operator, value });
return chosenHandler(builderClient, operator, value);
}

isOperatorHandler(
queryBuilder: Knex.QueryBuilder,
params: { field: IFieldInstance; operator: IFilterOperator; value: IFilterValue }
builderClient: Knex.QueryBuilder,
_operator: IFilterOperator,
value: IFilterValue
): Knex.QueryBuilder {
const { field, value } = params;
const parseValue = field.cellValueType === CellValueType.Number ? Number(value) : value;
const parseValue = this.field.cellValueType === CellValueType.Number ? Number(value) : value;

queryBuilder.where(field.dbFieldName, parseValue);
return queryBuilder;
builderClient.where(this.columnName, parseValue);
return builderClient;
}

isExactlyOperatorHandler(
_queryBuilder: Knex.QueryBuilder,
_params: { field: IFieldInstance; operator: IFilterOperator; value: IFilterValue }
_builderClient: Knex.QueryBuilder,
_operator: IFilterOperator,
_value: IFilterValue
): Knex.QueryBuilder {
throw new NotImplementedException();
}

abstract isNotOperatorHandler(
queryBuilder: Knex.QueryBuilder,
params: { field: IFieldInstance; operator: IFilterOperator; value: IFilterValue }
builderClient: Knex.QueryBuilder,
operator: IFilterOperator,
value: IFilterValue
): Knex.QueryBuilder;

containsOperatorHandler(
queryBuilder: Knex.QueryBuilder,
params: { field: IFieldInstance; operator: IFilterOperator; value: IFilterValue }
builderClient: Knex.QueryBuilder,
_operator: IFilterOperator,
value: IFilterValue
): Knex.QueryBuilder {
const { field, value } = params;

queryBuilder.where(field.dbFieldName, 'LIKE', `%${value}%`);
return queryBuilder;
builderClient.where(this.columnName, 'LIKE', `%${value}%`);
return builderClient;
}

abstract doesNotContainOperatorHandler(
queryBuilder: Knex.QueryBuilder,
params: { field: IFieldInstance; operator: IFilterOperator; value: IFilterValue }
builderClient: Knex.QueryBuilder,
operator: IFilterOperator,
value: IFilterValue
): Knex.QueryBuilder;

isGreaterOperatorHandler(
queryBuilder: Knex.QueryBuilder,
params: { field: IFieldInstance; operator: IFilterOperator; value: IFilterValue }
builderClient: Knex.QueryBuilder,
operator: IFilterOperator,
value: IFilterValue
): Knex.QueryBuilder {
const { field, value } = params;
const parseValue = field.cellValueType === CellValueType.Number ? Number(value) : value;
const { cellValueType } = this.field;
const parseValue = cellValueType === CellValueType.Number ? Number(value) : value;

queryBuilder.where(field.dbFieldName, '>', parseValue);
return queryBuilder;
builderClient.where(this.columnName, '>', parseValue);
return builderClient;
}

isGreaterEqualOperatorHandler(
queryBuilder: Knex.QueryBuilder,
params: { field: IFieldInstance; operator: IFilterOperator; value: IFilterValue }
builderClient: Knex.QueryBuilder,
operator: IFilterOperator,
value: IFilterValue
): Knex.QueryBuilder {
const { field, value } = params;
const parseValue = field.cellValueType === CellValueType.Number ? Number(value) : value;
const { cellValueType } = this.field;
const parseValue = cellValueType === CellValueType.Number ? Number(value) : value;

queryBuilder.where(field.dbFieldName, '>=', parseValue);
return queryBuilder;
builderClient.where(this.columnName, '>=', parseValue);
return builderClient;
}

isLessOperatorHandler(
queryBuilder: Knex.QueryBuilder,
params: { field: IFieldInstance; operator: IFilterOperator; value: IFilterValue }
builderClient: Knex.QueryBuilder,
operator: IFilterOperator,
value: IFilterValue
): Knex.QueryBuilder {
const { field, value } = params;
const parseValue = field.cellValueType === CellValueType.Number ? Number(value) : value;
const { cellValueType } = this.field;
const parseValue = cellValueType === CellValueType.Number ? Number(value) : value;

queryBuilder.where(field.dbFieldName, '<', parseValue);
return queryBuilder;
builderClient.where(this.columnName, '<', parseValue);
return builderClient;
}

isLessEqualOperatorHandler(
queryBuilder: Knex.QueryBuilder,
params: { field: IFieldInstance; operator: IFilterOperator; value: IFilterValue }
builderClient: Knex.QueryBuilder,
operator: IFilterOperator,
value: IFilterValue
): Knex.QueryBuilder {
const { field, value } = params;
const parseValue = field.cellValueType === CellValueType.Number ? Number(value) : value;
const { cellValueType } = this.field;
const parseValue = cellValueType === CellValueType.Number ? Number(value) : value;

queryBuilder.where(field.dbFieldName, '<=', parseValue);
return queryBuilder;
builderClient.where(this.columnName, '<=', parseValue);
return builderClient;
}

isAnyOfOperatorHandler(
queryBuilder: Knex.QueryBuilder,
params: { field: IFieldInstance; operator: IFilterOperator; value: IFilterValue }
builderClient: Knex.QueryBuilder,
operator: IFilterOperator,
value: IFilterValue
): Knex.QueryBuilder {
const { field, value } = params;
const valueList = literalValueListSchema.parse(value);

queryBuilder.whereIn(field.dbFieldName, [...valueList]);
return queryBuilder;
builderClient.whereIn(this.columnName, [...valueList]);
return builderClient;
}

abstract isNoneOfOperatorHandler(
queryBuilder: Knex.QueryBuilder,
params: { field: IFieldInstance; operator: IFilterOperator; value: IFilterValue }
builderClient: Knex.QueryBuilder,
operator: IFilterOperator,
value: IFilterValue
): Knex.QueryBuilder;

hasAllOfOperatorHandler(
_queryBuilder: Knex.QueryBuilder,
_params: { field: IFieldInstance; operator: IFilterOperator; value: IFilterValue }
_builderClient: Knex.QueryBuilder,
_operator: IFilterOperator,
_value: IFilterValue
): Knex.QueryBuilder {
throw new NotImplementedException();
}

isWithInOperatorHandler(
_queryBuilder: Knex.QueryBuilder,
_params: { field: IFieldInstance; operator: IFilterOperator; value: IFilterValue }
_builderClient: Knex.QueryBuilder,
_operator: IFilterOperator,
_value: IFilterValue
): Knex.QueryBuilder {
throw new NotImplementedException();
}

isEmptyOperatorHandler(
queryBuilder: Knex.QueryBuilder,
params: { field: IFieldInstance; operator: IFilterOperator; value: IFilterValue }
builderClient: Knex.QueryBuilder,
_operator: IFilterOperator,
_value: IFilterValue
): Knex.QueryBuilder {
const { field } = params;

queryBuilder.whereNull(field.dbFieldName);
return queryBuilder;
builderClient.whereNull(this.columnName);
return builderClient;
}

isNotEmptyOperatorHandler(
queryBuilder: Knex.QueryBuilder,
params: { field: IFieldInstance; operator: IFilterOperator; value: IFilterValue }
builderClient: Knex.QueryBuilder,
_operator: IFilterOperator,
_value: IFilterValue
): Knex.QueryBuilder {
const { field } = params;

queryBuilder.whereNotNull(field.dbFieldName);
return queryBuilder;
builderClient.whereNotNull(this.columnName);
return builderClient;
}

protected createSqlPlaceholders(values: unknown[]): string {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import type { IFilterOperator, IFilterValue } from '@teable-group/core';
import type { Knex } from 'knex';
import type { IFieldInstance } from '../../features/field/model/factory';

export type ICellValueFilterHandler = (
queryBuilder: Knex.QueryBuilder,
params: {
field: IFieldInstance;
operator: IFilterOperator;
value: IFilterValue;
}
builderClient: Knex.QueryBuilder,
operator: IFilterOperator,
value: IFilterValue
) => Knex.QueryBuilder;

export interface ICellValueFilterInterface {
Expand Down
Loading