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

teableio / teable / 11834174390

14 Nov 2024 09:17AM UTC coverage: 84.266% (-0.1%) from 84.415%
11834174390

Pull #1087

github

web-flow
Merge d6ff1e32b into aef87a69c
Pull Request #1087: fix: import and search bugs

5970 of 6273 branches covered (95.17%)

21 of 43 new or added lines in 3 files covered. (48.84%)

70 existing lines in 2 files now uncovered.

39450 of 46816 relevant lines covered (84.27%)

1699.76 hits per line

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

23.88
/apps/nestjs-backend/src/features/setting/admin.service.ts
1
import { Session } from 'node:inspector';
2✔
2
import { Readable } from 'node:stream';
2✔
3
import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common';
2✔
4
import { PrismaService } from '@teable/db-main-prisma';
2✔
5
import { PluginStatus, UploadType } from '@teable/openapi';
2✔
6
import { Response } from 'express';
2✔
7
import { Knex } from 'knex';
2✔
8
import { InjectModel } from 'nest-knexjs';
2✔
9
import { Timing } from '../../utils/timing';
2✔
10
import { AttachmentsCropQueueProcessor } from '../attachments/attachments-crop.processor';
2✔
11
import StorageAdapter from '../attachments/plugins/adapter';
2✔
12

2✔
13
@Injectable()
2✔
14
export class AdminService {
2✔
15
  private readonly logger = new Logger(AdminService.name);
95✔
16
  constructor(
95✔
17
    private readonly prismaService: PrismaService,
95✔
18
    @InjectModel('CUSTOM_KNEX') private readonly knex: Knex,
95✔
19
    private readonly attachmentsCropQueueProcessor: AttachmentsCropQueueProcessor
95✔
20
  ) {}
95✔
21

95✔
22
  async publishPlugin(pluginId: string) {
95✔
23
    return this.prismaService.plugin.update({
12✔
24
      where: { id: pluginId, status: PluginStatus.Reviewing },
12✔
25
      data: { status: PluginStatus.Published },
12✔
26
    });
12✔
27
  }
12✔
28

95✔
29
  async repairTableAttachmentThumbnail() {
95✔
30
    // once handle 1000 attachments
×
31
    const take = 1000;
×
32
    let total = 0;
×
33
    for (let skip = 0; ; skip += take) {
×
34
      const sqlNative = this.knex('attachments_table')
×
35
        .select(
×
36
          'attachments.token',
×
37
          'attachments.height',
×
38
          'attachments.mimetype',
×
39
          'attachments.path'
×
40
        )
×
41
        .leftJoin('attachments', 'attachments_table.token', 'attachments.token')
×
42
        .whereNotNull('attachments.height')
×
43
        .whereNull('attachments.deleted_time')
×
44
        .whereNull('attachments.thumbnail_path')
×
45
        .limit(take)
×
46
        .offset(skip)
×
47
        .toSQL()
×
48
        .toNative();
×
49
      const attachments = await this.prismaService.$queryRawUnsafe<
×
50
        { token: string; height?: number; mimetype: string; path: string }[]
×
51
      >(sqlNative.sql, ...sqlNative.bindings);
×
52
      this.logger.log('attachments', attachments, sqlNative.sql);
×
53
      if (attachments.length === 0) {
×
54
        break;
×
55
      }
×
56
      total += attachments.length;
×
57
      await this.attachmentsCropQueueProcessor.queue.addBulk(
×
58
        attachments.map((attachment) => ({
×
59
          name: 'admin_attachment_crop_image',
×
60
          data: {
×
61
            ...attachment,
×
62
            bucket: StorageAdapter.getBucket(UploadType.Table),
×
63
          },
×
64
        }))
×
65
      );
×
UNCOV
66
      this.logger.log(`Processed ${attachments.length} attachments`);
×
UNCOV
67
    }
×
UNCOV
68
    this.logger.log(`Total processed ${total} attachments`);
×
UNCOV
69
  }
×
70

95✔
71
  @Timing()
95✔
UNCOV
72
  async getHeapSnapshot(res: Response) {
×
UNCOV
73
    const podName = process.env.HOSTNAME || 'unknown';
×
UNCOV
74
    const session = new Session();
×
UNCOV
75
    const timestamp = new Date().toISOString();
×
UNCOV
76
    const filename = `heap-${podName}-${timestamp}.heapsnapshot`;
×
UNCOV
77
    try {
×
UNCOV
78
      const snapshotStream = new Readable({
×
UNCOV
79
        // eslint-disable-next-line @typescript-eslint/no-empty-function
×
UNCOV
80
        read() {},
×
UNCOV
81
      });
×
UNCOV
82

×
UNCOV
83
      res.setHeader('Content-Type', 'application/octet-stream');
×
UNCOV
84
      res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
×
UNCOV
85

×
UNCOV
86
      session.connect();
×
UNCOV
87
      session.on('HeapProfiler.addHeapSnapshotChunk', (m) => {
×
UNCOV
88
        snapshotStream.push(m.params.chunk);
×
UNCOV
89
      });
×
UNCOV
90

×
UNCOV
91
      const snapshotPromise = new Promise<void>((resolve, reject) => {
×
UNCOV
92
        session.post('HeapProfiler.takeHeapSnapshot', undefined, (err) => {
×
UNCOV
93
          if (err) {
×
UNCOV
94
            reject(err);
×
UNCOV
95
          } else {
×
UNCOV
96
            snapshotStream.push(null);
×
UNCOV
97
            resolve();
×
UNCOV
98
          }
×
UNCOV
99
        });
×
UNCOV
100
      });
×
UNCOV
101

×
UNCOV
102
      snapshotStream.on('error', (error) => {
×
UNCOV
103
        this.logger.error(`Stream error for pod ${podName}:`, error);
×
UNCOV
104
        throw new InternalServerErrorException(`Stream error: ${error.message}`);
×
UNCOV
105
      });
×
UNCOV
106

×
UNCOV
107
      snapshotStream.pipe(res);
×
UNCOV
108

×
UNCOV
109
      await new Promise<void>((resolve, reject) => {
×
UNCOV
110
        res.on('finish', () => {
×
UNCOV
111
          this.logger.log(`Heap snapshot streaming completed for pod ${podName}`);
×
UNCOV
112
          resolve();
×
UNCOV
113
        });
×
UNCOV
114

×
UNCOV
115
        res.on('error', (error) => {
×
UNCOV
116
          this.logger.error(`Response error for pod ${podName}:`, error);
×
UNCOV
117
          reject(error);
×
UNCOV
118
        });
×
UNCOV
119

×
UNCOV
120
        snapshotStream.on('error', reject);
×
UNCOV
121
      });
×
UNCOV
122

×
UNCOV
123
      await snapshotPromise;
×
UNCOV
124
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
×
UNCOV
125
    } catch (error: any) {
×
UNCOV
126
      throw new InternalServerErrorException(
×
UNCOV
127
        `Failed to get heap snapshot: ${error.message}, podName: ${podName}, timestamp: ${timestamp}`
×
UNCOV
128
      );
×
UNCOV
129
    } finally {
×
UNCOV
130
      session.disconnect();
×
UNCOV
131
      this.logger.log(`Session disconnected for pod ${podName}`);
×
UNCOV
132
    }
×
UNCOV
133
  }
×
134
}
95✔
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