Skip to content

Commit

Permalink
feat: long text
Browse files Browse the repository at this point in the history
  • Loading branch information
Sky-FE committed Oct 11, 2023
1 parent ae5d2c9 commit d89d072
Show file tree
Hide file tree
Showing 32 changed files with 649 additions and 34 deletions.
17 changes: 17 additions & 0 deletions apps/nestjs-backend/src/features/field/field-supplement.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
DateFieldCore,
CheckboxFieldCore,
RatingFieldCore,
LongTextFieldCore,
} from '@teable-group/core';
import { PrismaService } from '@teable-group/db-main-prisma';
import knex from 'knex';
Expand Down Expand Up @@ -461,6 +462,18 @@ export class FieldSupplementService implements ISupplementService {
};
}

private prepareLongTextField(field: IFieldRo) {
const { name, options } = field;

return {
...field,
name: name ?? 'Notes',
options: options ?? LongTextFieldCore.defaultOptions(),
cellValueType: CellValueType.String,
dbFieldType: DbFieldType.Text,
};
}

private prepareNumberField(field: IFieldRo) {
const { name, options } = field;

Expand Down Expand Up @@ -581,6 +594,8 @@ export class FieldSupplementService implements ISupplementService {
return this.prepareFormulaField(fieldRo, batchFieldVos);
case FieldType.SingleLineText:
return this.prepareSingleTextField(fieldRo);
case FieldType.LongText:
return this.prepareLongTextField(fieldRo);
case FieldType.Number:
return this.prepareNumberField(fieldRo);
case FieldType.Rating:
Expand Down Expand Up @@ -619,6 +634,8 @@ export class FieldSupplementService implements ISupplementService {
return this.prepareUpdateFormulaField(fieldRo, oldFieldVo);
case FieldType.SingleLineText:
return this.prepareSingleTextField(fieldRo);
case FieldType.LongText:
return this.prepareLongTextField(fieldRo);
case FieldType.Number:
return this.prepareNumberField(fieldRo);
case FieldType.Rating:
Expand Down
4 changes: 3 additions & 1 deletion apps/nestjs-backend/src/features/field/model/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { CheckboxFieldDto } from './field-dto/checkbox-field.dto';
import { DateFieldDto } from './field-dto/date-field.dto';
import { FormulaFieldDto } from './field-dto/formula-field.dto';
import { LinkFieldDto } from './field-dto/link-field.dto';
import { LongTextFieldDto } from './field-dto/long-text-field.dto';
import { MultipleSelectFieldDto } from './field-dto/multiple-select-field.dto';
import { NumberFieldDto } from './field-dto/number-field.dto';
import { RatingFieldDto } from './field-dto/rating-field.dto';
Expand Down Expand Up @@ -45,6 +46,8 @@ export function createFieldInstanceByVo(field: IFieldVo) {
switch (field.type) {
case FieldType.SingleLineText:
return plainToInstance(SingleLineTextFieldDto, field);
case FieldType.LongText:
return plainToInstance(LongTextFieldDto, field);
case FieldType.Number:
return plainToInstance(NumberFieldDto, field);
case FieldType.SingleSelect:
Expand All @@ -69,7 +72,6 @@ export function createFieldInstanceByVo(field: IFieldVo) {
case FieldType.CreatedBy:
case FieldType.Email:
case FieldType.LastModifiedBy:
case FieldType.LongText:
case FieldType.PhoneNumber:
case FieldType.URL:
case FieldType.User:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { LongTextFieldCore } from '@teable-group/core';
import type { IFieldBase } from '../field-base';

export class LongTextFieldDto extends LongTextFieldCore implements IFieldBase {
convertCellValue2DBValue(value: unknown): unknown {
if (this.isMultipleCellValue) {
return JSON.stringify(value);
}
return value;
}

convertDBValue2CellValue(value: unknown): unknown {
if (this.isMultipleCellValue) {
return value && JSON.parse(value as string);
}
return value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,7 @@ export class FieldConvertingService {
private async basalConvert(tableId: string, newField: IFieldInstance, oldField: IFieldInstance) {
// simple value type change is not need to convert
if (
oldField.type !== FieldType.LongText &&
newField.cellValueType === oldField.cellValueType &&
newField.isMultipleCellValue !== true &&
oldField.isMultipleCellValue !== true &&
Expand Down
281 changes: 281 additions & 0 deletions apps/nestjs-backend/test/field-converting.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,287 @@ describe('OpenAPI Freely perform column transformations (e2e)', () => {
});
});

describe('convert long text field', () => {
const sourceFieldRo: IFieldRo = {
name: 'LongTextField',
type: FieldType.LongText,
};

it('should convert long text to text', async () => {
const newFieldRo: IFieldRo = {
type: FieldType.SingleLineText,
};
const { newField, values } = await expectUpdate(table1, sourceFieldRo, newFieldRo, [
'1 2 3',
'x\ny\nz',
]);
expect(newField).toMatchObject({
cellValueType: CellValueType.String,
dbFieldType: DbFieldType.Text,
name: 'LongTextField',
type: FieldType.SingleLineText,
});
expect(values[0]).toEqual('1 2 3');
expect(values[1]).toEqual('x y z');
});

it('should convert long text to number', async () => {
const newFieldRo: IFieldRo = {
type: FieldType.Number,
};
const { newField, values } = await expectUpdate(table1, sourceFieldRo, newFieldRo, [
'1',
'x',
]);
expect(newField).toMatchObject({
cellValueType: CellValueType.Number,
dbFieldType: DbFieldType.Real,
name: 'LongTextField 2',
type: FieldType.Number,
});
expect(values[0]).toEqual(1);
expect(values[1]).toEqual(undefined);
});

it('should convert long text to single select', async () => {
const newFieldRo: IFieldRo = {
type: FieldType.SingleSelect,
options: {
choices: [{ name: 'A', color: Colors.Cyan }],
},
};
const { newField, values } = await expectUpdate(table1, sourceFieldRo, newFieldRo, [
'A',
'B',
'Hello\nWorld',
]);
expect(newField).toMatchObject({
cellValueType: CellValueType.String,
dbFieldType: DbFieldType.Text,
options: {
choices: [{ name: 'A', color: Colors.Cyan }, { name: 'B' }, { name: 'Hello World' }],
},
type: FieldType.SingleSelect,
});
expect(values[0]).toEqual('A');
expect(values[1]).toEqual('B');
expect(values[2]).toEqual('Hello World');
});

it('should convert long text to multiple select', async () => {
const newFieldRo: IFieldRo = {
type: FieldType.MultipleSelect,
options: {
choices: [
{ name: 'x', color: Colors.Blue },
{ name: 'y', color: Colors.Red },
],
},
};
const { newField, values } = await expectUpdate(table1, sourceFieldRo, newFieldRo, [
'x',
'x, y',
'x\nz',
]);
expect(newField).toMatchObject({
cellValueType: CellValueType.String,
isMultipleCellValue: true,
dbFieldType: DbFieldType.Json,
options: {
choices: [
{ name: 'x', color: Colors.Blue },
{ name: 'y', color: Colors.Red },
{ name: 'z' },
],
},
type: FieldType.MultipleSelect,
});
expect(values[0]).toEqual(['x']);
expect(values[1]).toEqual(['x', 'y']);
expect(values[2]).toEqual(['x', 'z']);
});

it('should convert long text to attachment', async () => {
const newFieldRo: IFieldRo = {
type: FieldType.Attachment,
};
const { newField, values } = await expectUpdate(table1, sourceFieldRo, newFieldRo, [
'x',
'x\ny',
]);
expect(newField).toMatchObject({
cellValueType: CellValueType.String,
isMultipleCellValue: true,
dbFieldType: DbFieldType.Json,
type: FieldType.Attachment,
});
expect(values[0]).toEqual(undefined);
expect(values[1]).toEqual(undefined);
});

it('should convert long text to checkbox', async () => {
const newFieldRo: IFieldRo = {
type: FieldType.Checkbox,
};
const { newField, values } = await expectUpdate(table1, sourceFieldRo, newFieldRo, [
'x',
null,
]);
expect(newField).toMatchObject({
cellValueType: CellValueType.Boolean,
dbFieldType: DbFieldType.Integer,
type: FieldType.Checkbox,
});
expect(values[0]).toEqual(true);
expect(values[1]).toEqual(undefined);
});

it('should convert long text to date', async () => {
const newFieldRo: IFieldRo = {
type: FieldType.Date,
options: {
formatting: {
date: 'M/D/YYYY',
time: TimeFormatting.None,
timeZone: 'GMT',
},
},
};
const { newField, values } = await expectUpdate(table1, sourceFieldRo, newFieldRo, [
'x',
'2023-08-31T08:32:32.117Z',
]);
expect(newField).toMatchObject({
cellValueType: CellValueType.DateTime,
dbFieldType: DbFieldType.DateTime,
type: FieldType.Date,
});
expect(values[0]).toEqual(undefined);
expect(values[1]).toEqual('2023-08-31T08:32:32.117Z');
});

it('should convert long text to formula', async () => {
const newFieldRo: IFieldRo = {
type: FieldType.Formula,
options: {
expression: '1',
},
};
const { newField, values } = await expectUpdate(table1, sourceFieldRo, newFieldRo, [
'x',
null,
]);
expect(newField).toMatchObject({
cellValueType: CellValueType.Number,
dbFieldType: DbFieldType.Real,
type: FieldType.Formula,
isComputed: true,
});
expect(values[0]).toEqual(1);
expect(values[1]).toEqual(1);
});

it('should convert long text to many-one rollup', async () => {
const linkFieldRo: IFieldRo = {
type: FieldType.Link,
options: {
relationship: Relationship.ManyOne,
foreignTableId: table2.id,
},
};
const linkField = await createField(request, table1.id, linkFieldRo);
// set primary key 'x' in table2
await updateRecordByApi(request, table2.id, table2.records[0].id, table2.fields[0].id, 'x');
// add 2 link record
await updateRecordByApi(request, table1.id, table1.records[0].id, linkField.id, {
id: table2.records[0].id,
});
await updateRecordByApi(request, table1.id, table1.records[1].id, linkField.id, {
id: table2.records[0].id,
});

const newFieldRo: IFieldRo = {
type: FieldType.Rollup,
options: {
expression: 'countall({values})',
},
lookupOptions: {
foreignTableId: table2.id,
lookupFieldId: table2.fields[0].id,
linkFieldId: linkField.id,
},
};

const { newField, values } = await expectUpdate(table1, sourceFieldRo, newFieldRo, [null]);
expect(newField).toMatchObject({
cellValueType: CellValueType.Number,
dbFieldType: DbFieldType.Real,
type: FieldType.Rollup,
options: {
expression: 'countall({values})',
},
lookupOptions: {
foreignTableId: table2.id,
lookupFieldId: table2.fields[0].id,
linkFieldId: linkField.id,
},
});

expect(values[0]).toEqual(1);
});

it('should convert long text to one-many rollup', async () => {
const linkFieldRo: IFieldRo = {
type: FieldType.Link,
options: {
relationship: Relationship.OneMany,
foreignTableId: table2.id,
},
};
const linkField = await createField(request, table1.id, linkFieldRo);
// set primary key in table2
await updateRecordByApi(request, table2.id, table2.records[0].id, table2.fields[0].id, 'gg');
// add 2 link record
await updateRecordByApi(request, table1.id, table1.records[0].id, linkField.id, [
{
id: table2.records[0].id,
},
{
id: table2.records[1].id,
},
]);

const newFieldRo: IFieldRo = {
type: FieldType.Rollup,
options: {
expression: 'countall({values})',
},
lookupOptions: {
foreignTableId: table2.id,
lookupFieldId: table2.fields[0].id,
linkFieldId: linkField.id,
},
};

const { newField, values } = await expectUpdate(table1, sourceFieldRo, newFieldRo, [null]);
expect(newField).toMatchObject({
cellValueType: CellValueType.Number,
dbFieldType: DbFieldType.Real,
type: FieldType.Rollup,
options: {
expression: 'countall({values})',
},
lookupOptions: {
foreignTableId: table2.id,
lookupFieldId: table2.fields[0].id,
linkFieldId: linkField.id,
},
});

expect(values[0]).toEqual(2);
});
});

describe('convert select field', () => {
it('should convert select to number', async () => {
const sourceFieldRo: IFieldRo = {
Expand Down
Loading

0 comments on commit d89d072

Please sign in to comment.