• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

teableio / teable / 10523150885

23 Aug 2024 09:13AM UTC coverage: 83.355% (+0.3%) from 83.022%
10523150885

Pull #810

github

web-flow
Merge 65963bcd5 into c246febe5
Pull Request #810: fix: import relative

4644 of 4862 branches covered (95.52%)

130 of 149 new or added lines in 6 files covered. (87.25%)

36 existing lines in 4 files now uncovered.

30804 of 36955 relevant lines covered (83.36%)

1214.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.82
/apps/nestjs-backend/src/features/field/open-api/field-open-api.service.ts
1
import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common';
4✔
2
import { FieldOpBuilder, IFieldRo } from '@teable/core';
4✔
3
import type { IFieldVo, IConvertFieldRo, IUpdateFieldRo, IOtOperation } from '@teable/core';
4✔
4
import { PrismaService } from '@teable/db-main-prisma';
4✔
5
import { instanceToPlain } from 'class-transformer';
4✔
6
import { ClsService } from 'nestjs-cls';
4✔
7
import { ThresholdConfig, IThresholdConfig } from '../../../configs/threshold.config';
4✔
8
import type { IClsStore } from '../../../types/cls';
4✔
9
import { Timing } from '../../../utils/timing';
4✔
10
import { FieldCalculationService } from '../../calculation/field-calculation.service';
4✔
11
import { GraphService } from '../../graph/graph.service';
4✔
12
import { FieldConvertingService } from '../field-calculate/field-converting.service';
4✔
13
import { FieldCreatingService } from '../field-calculate/field-creating.service';
4✔
14
import { FieldDeletingService } from '../field-calculate/field-deleting.service';
4✔
15
import { FieldSupplementService } from '../field-calculate/field-supplement.service';
4✔
16
import { FieldViewSyncService } from '../field-calculate/field-view-sync.service';
4✔
17
import { FieldService } from '../field.service';
4✔
18
import { createFieldInstanceByVo } from '../model/factory';
4✔
19

4✔
20
@Injectable()
4✔
21
export class FieldOpenApiService {
4✔
22
  private logger = new Logger(FieldOpenApiService.name);
92✔
23
  constructor(
92✔
24
    private readonly graphService: GraphService,
92✔
25
    private readonly prismaService: PrismaService,
92✔
26
    private readonly fieldService: FieldService,
92✔
27
    private readonly fieldCreatingService: FieldCreatingService,
92✔
28
    private readonly fieldDeletingService: FieldDeletingService,
92✔
29
    private readonly fieldConvertingService: FieldConvertingService,
92✔
30
    private readonly fieldSupplementService: FieldSupplementService,
92✔
31
    private readonly fieldCalculationService: FieldCalculationService,
92✔
32
    private readonly fieldViewSyncService: FieldViewSyncService,
92✔
33
    private readonly cls: ClsService<IClsStore>,
92✔
34
    @ThresholdConfig() private readonly thresholdConfig: IThresholdConfig
92✔
35
  ) {}
92✔
36

92✔
37
  async planField(tableId: string, fieldId: string) {
92✔
38
    return await this.graphService.planField(tableId, fieldId);
8✔
39
  }
8✔
40

92✔
41
  async planFieldCreate(tableId: string, fieldRo: IFieldRo) {
92✔
42
    return await this.graphService.planFieldCreate(tableId, fieldRo);
4✔
43
  }
4✔
44

92✔
45
  // TODO add delete relative check
92✔
46
  async planFieldConvert(tableId: string, fieldId: string, updateFieldRo: IConvertFieldRo) {
92✔
47
    return await this.graphService.planFieldConvert(tableId, fieldId, updateFieldRo);
10✔
48
  }
10✔
49

92✔
50
  @Timing()
92✔
51
  async createField(tableId: string, fieldRo: IFieldRo) {
867✔
52
    const fieldVo = await this.fieldSupplementService.prepareCreateField(tableId, fieldRo);
867✔
53
    const fieldInstance = createFieldInstanceByVo(fieldVo);
865✔
54
    const newFields = await this.prismaService.$tx(async () => {
865✔
55
      return await this.fieldCreatingService.alterCreateField(tableId, fieldInstance);
865✔
56
    });
865✔
57

829✔
58
    await this.prismaService.$tx(
829✔
59
      async () => {
829✔
60
        for (const { tableId, field } of newFields) {
829✔
61
          if (field.isComputed) {
1,052✔
62
            await this.fieldCalculationService.calculateFields(tableId, [field.id]);
307✔
63
            await this.fieldService.resolvePending(tableId, [field.id]);
307✔
64
          }
307✔
65
        }
1,052✔
66
      },
829✔
67
      { timeout: this.thresholdConfig.bigTransactionTimeout }
829✔
68
    );
829✔
69

829✔
70
    return fieldVo;
829✔
71
  }
829✔
72

92✔
73
  async deleteField(tableId: string, fieldId: string) {
92✔
74
    const field = await this.fieldDeletingService.getField(tableId, fieldId);
42✔
75
    if (!field) {
42✔
UNCOV
76
      throw new NotFoundException(`Field ${fieldId} not found`);
×
UNCOV
77
    }
×
78

42✔
79
    await this.prismaService.$tx(async () => {
42✔
80
      await this.fieldViewSyncService.deleteViewRelativeByFields(tableId, [fieldId]);
42✔
81
      await this.fieldDeletingService.alterDeleteField(tableId, field);
42✔
82
    });
40✔
83
  }
40✔
84

92✔
85
  private async updateUniqProperty(
92✔
86
    tableId: string,
×
87
    fieldId: string,
×
88
    key: 'name' | 'dbFieldName',
×
89
    value: string
×
90
  ) {
×
91
    const result = await this.prismaService.field
×
92
      .findFirstOrThrow({
×
93
        where: { id: fieldId, deletedTime: null },
×
94
        select: { [key]: true },
×
95
      })
×
96
      .catch(() => {
×
97
        throw new NotFoundException(`Field ${fieldId} not found`);
×
98
      });
×
99

×
100
    const hasDuplicated = await this.prismaService.field.findFirst({
×
101
      where: { tableId, [key]: value, deletedTime: null },
×
102
      select: { id: true },
×
103
    });
×
104

×
105
    if (hasDuplicated) {
×
106
      throw new BadRequestException(`Field ${key} ${value} already exists`);
×
107
    }
×
108

×
109
    return FieldOpBuilder.editor.setFieldProperty.build({
×
110
      key,
×
UNCOV
111
      oldValue: result[key],
×
UNCOV
112
      newValue: value,
×
113
    });
×
114
  }
×
115

92✔
116
  async updateField(tableId: string, fieldId: string, updateFieldRo: IUpdateFieldRo) {
92✔
117
    const ops: IOtOperation[] = [];
×
118
    if (updateFieldRo.name) {
×
119
      const op = await this.updateUniqProperty(tableId, fieldId, 'name', updateFieldRo.name);
×
120
      ops.push(op);
×
121
    }
×
122

×
123
    if (updateFieldRo.dbFieldName) {
×
124
      const op = await this.updateUniqProperty(
×
125
        tableId,
×
126
        fieldId,
×
127
        'dbFieldName',
×
128
        updateFieldRo.dbFieldName
×
129
      );
×
130
      ops.push(op);
×
131
    }
×
132

×
133
    if (updateFieldRo.description !== undefined) {
×
134
      const { description } = await this.prismaService.field
×
135
        .findFirstOrThrow({
×
136
          where: { id: fieldId, deletedTime: null },
×
137
          select: { description: true },
×
138
        })
×
139
        .catch(() => {
×
140
          throw new NotFoundException(`Field ${fieldId} not found`);
×
141
        });
×
142

×
143
      ops.push(
×
144
        FieldOpBuilder.editor.setFieldProperty.build({
×
145
          key: 'description',
×
146
          oldValue: description,
×
147
          newValue: updateFieldRo.description,
×
148
        })
×
149
      );
×
150
    }
×
151

×
UNCOV
152
    await this.prismaService.$tx(async () => {
×
UNCOV
153
      await this.fieldService.batchUpdateFields(tableId, [{ fieldId, ops }]);
×
UNCOV
154
    });
×
UNCOV
155
  }
×
156

92✔
157
  async convertField(
92✔
158
    tableId: string,
155✔
159
    fieldId: string,
155✔
160
    updateFieldRo: IConvertFieldRo
155✔
161
  ): Promise<IFieldVo> {
155✔
162
    // 1. stage analysis and collect field changes
155✔
163
    const { newField, oldField, modifiedOps, supplementChange, needSupplementFieldConstraint } =
155✔
164
      await this.fieldConvertingService.stageAnalysis(tableId, fieldId, updateFieldRo);
155✔
165
    this.cls.set('oldField', oldField);
153✔
166

153✔
167
    // 2. stage alter field
153✔
168
    await this.prismaService.$tx(async () => {
153✔
169
      await this.fieldViewSyncService.convertFieldRelative(tableId, newField, oldField);
153✔
170
      await this.fieldConvertingService.stageAlter(tableId, newField, oldField, modifiedOps);
153✔
171
      await this.fieldConvertingService.alterSupplementLink(
151✔
172
        tableId,
151✔
173
        newField,
151✔
174
        oldField,
151✔
175
        supplementChange
151✔
176
      );
151✔
177
    });
151✔
178

151✔
179
    // 3. stage apply record changes and calculate field
151✔
180
    await this.prismaService.$tx(
151✔
181
      async () => {
151✔
182
        await this.fieldConvertingService.stageCalculate(tableId, newField, oldField, modifiedOps);
151✔
183

151✔
184
        if (supplementChange) {
151✔
185
          const { tableId, newField, oldField } = supplementChange;
4✔
186
          await this.fieldConvertingService.stageCalculate(tableId, newField, oldField);
4✔
187
        }
4✔
188
      },
151✔
189
      { timeout: this.thresholdConfig.bigTransactionTimeout }
151✔
190
    );
151✔
191

151✔
192
    // 4. stage supplement field constraint
151✔
193
    if (needSupplementFieldConstraint) {
155✔
UNCOV
194
      await this.fieldConvertingService.supplementFieldConstraint(tableId, newField);
×
UNCOV
195
    }
✔
196

151✔
197
    return instanceToPlain(newField, { excludePrefixes: ['_'] }) as IFieldVo;
151✔
198
  }
151✔
199
}
92✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc