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

teableio / teable / 8389034568

22 Mar 2024 10:38AM UTC coverage: 79.934% (+51.7%) from 28.208%
8389034568

Pull #487

github

web-flow
Merge 3045b1f94 into a06c6afb1
Pull Request #487: refactor: move zod schema to openapi

3263 of 3860 branches covered (84.53%)

67 of 70 new or added lines in 23 files covered. (95.71%)

762 existing lines in 27 files now uncovered.

25152 of 31466 relevant lines covered (79.93%)

1188.31 hits per line

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

89.9
/apps/nestjs-backend/src/features/access-token/access-token.service.ts
1
import { Injectable, UnauthorizedException } from '@nestjs/common';
2✔
2
import type { AllActions } from '@teable/core';
2✔
3
import { generateAccessTokenId, getRandomString } from '@teable/core';
2✔
4
import { PrismaService } from '@teable/db-main-prisma';
2✔
5
import type {
2✔
6
  CreateAccessTokenRo,
2✔
7
  RefreshAccessTokenRo,
2✔
8
  UpdateAccessTokenRo,
2✔
9
} from '@teable/openapi';
2✔
10
import { ClsService } from 'nestjs-cls';
2✔
11
import type { IClsStore } from '../../types/cls';
2✔
12
import { getAccessToken } from './access-token.encryptor';
2✔
13

2✔
14
@Injectable()
2✔
15
export class AccessTokenService {
2✔
16
  constructor(
64✔
17
    private readonly prismaService: PrismaService,
64✔
18
    private readonly cls: ClsService<IClsStore>
64✔
19
  ) {}
64✔
20

64✔
21
  private transformAccessTokenEntity<
64✔
22
    T extends {
32✔
23
      description?: string | null;
32✔
24
      scopes: string;
32✔
25
      spaceIds: string | null;
32✔
26
      baseIds: string | null;
32✔
27
      createdTime?: Date;
32✔
28
      lastUsedTime?: Date | null;
32✔
29
      expiredTime?: Date;
32✔
30
    },
32✔
31
  >(accessTokenEntity: T) {
32✔
32
    const { scopes, spaceIds, baseIds, createdTime, lastUsedTime, expiredTime, description } =
32✔
33
      accessTokenEntity;
32✔
34
    return {
32✔
35
      ...accessTokenEntity,
32✔
36
      description: description || undefined,
32!
37
      scopes: JSON.parse(scopes) as AllActions[],
32✔
38
      spaceIds: spaceIds ? (JSON.parse(spaceIds) as string[]) : undefined,
32✔
39
      baseIds: baseIds ? (JSON.parse(baseIds) as string[]) : undefined,
32✔
40
      createdTime: createdTime?.toISOString(),
32✔
41
      lastUsedTime: lastUsedTime?.toISOString(),
32✔
42
      expiredTime: expiredTime?.toISOString(),
32✔
43
    };
32✔
44
  }
32✔
45

64✔
46
  async validate(splitAccessTokenObj: { accessTokenId: string; sign: string }) {
64✔
47
    const { accessTokenId, sign } = splitAccessTokenObj;
10✔
48

10✔
49
    const accessTokenEntity = await this.prismaService.txClient().accessToken.findUniqueOrThrow({
10✔
50
      where: { id: accessTokenId },
10✔
51
      select: {
10✔
52
        userId: true,
10✔
53
        id: true,
10✔
54
        sign: true,
10✔
55
        expiredTime: true,
10✔
56
      },
10✔
57
    });
10✔
58
    if (sign !== accessTokenEntity.sign) {
10!
UNCOV
59
      throw new UnauthorizedException('sign error');
×
UNCOV
60
    }
×
61
    // expiredTime 1ms tolerance
10✔
62
    if (accessTokenEntity.expiredTime.getTime() < Date.now() + 1000) {
10!
UNCOV
63
      throw new UnauthorizedException('token expired');
×
UNCOV
64
    }
×
65
    await this.prismaService.txClient().accessToken.update({
10✔
66
      where: { id: accessTokenId },
10✔
67
      data: { lastUsedTime: new Date().toISOString() },
10✔
68
    });
10✔
69

10✔
70
    return {
10✔
71
      userId: accessTokenEntity.userId,
10✔
72
      accessTokenId: accessTokenEntity.id,
10✔
73
    };
10✔
74
  }
10✔
75

64✔
76
  async listAccessToken() {
64✔
77
    const userId = this.cls.get('user.id');
4✔
78
    const list = await this.prismaService.accessToken.findMany({
4✔
79
      where: { userId },
4✔
80
      select: {
4✔
81
        id: true,
4✔
82
        name: true,
4✔
83
        description: true,
4✔
84
        scopes: true,
4✔
85
        spaceIds: true,
4✔
86
        baseIds: true,
4✔
87
        createdTime: true,
4✔
88
        expiredTime: true,
4✔
89
        lastUsedTime: true,
4✔
90
      },
4✔
91
      orderBy: { createdTime: 'desc' },
4✔
92
    });
4✔
93
    return list.map(this.transformAccessTokenEntity);
4✔
94
  }
4✔
95

64✔
96
  async createAccessToken(createAccessToken: CreateAccessTokenRo) {
64✔
97
    const userId = this.cls.get('user.id');
14✔
98
    const { name, description, scopes, spaceIds, baseIds, expiredTime } = createAccessToken;
14✔
99
    const id = generateAccessTokenId();
14✔
100
    const sign = getRandomString(16);
14✔
101
    const accessTokenEntity = await this.prismaService.accessToken.create({
14✔
102
      data: {
14✔
103
        id,
14✔
104
        name,
14✔
105
        description,
14✔
106
        scopes: JSON.stringify(scopes),
14✔
107
        spaceIds: spaceIds === null ? null : JSON.stringify(spaceIds),
14!
108
        baseIds: baseIds === null ? null : JSON.stringify(baseIds),
14!
109
        userId,
14✔
110
        sign,
14✔
111
        expiredTime: new Date(expiredTime).toISOString(),
14✔
112
      },
14✔
113
      select: {
14✔
114
        id: true,
14✔
115
        name: true,
14✔
116
        description: true,
14✔
117
        scopes: true,
14✔
118
        spaceIds: true,
14✔
119
        baseIds: true,
14✔
120
        expiredTime: true,
14✔
121
        createdTime: true,
14✔
122
        lastUsedTime: true,
14✔
123
      },
14✔
124
    });
14✔
125
    return {
14✔
126
      ...this.transformAccessTokenEntity(accessTokenEntity),
14✔
127
      token: getAccessToken(id, sign),
14✔
128
    };
14✔
129
  }
14✔
130

64✔
131
  async deleteAccessToken(id: string) {
64✔
132
    const userId = this.cls.get('user.id');
14✔
133
    await this.prismaService.accessToken.delete({
14✔
134
      where: { id, userId },
14✔
135
    });
14✔
136
  }
14✔
137

64✔
138
  async refreshAccessToken(id: string, refreshAccessTokenRo?: RefreshAccessTokenRo) {
64✔
139
    const userId = this.cls.get('user.id');
2✔
140

2✔
141
    const sign = getRandomString(16);
2✔
142
    const expiredTime = refreshAccessTokenRo?.expiredTime;
2✔
143
    const accessTokenEntity = await this.prismaService.accessToken.update({
2✔
144
      where: { id, userId },
2✔
145
      data: {
2✔
146
        sign,
2✔
147
        expiredTime: expiredTime ? new Date(expiredTime).toISOString() : undefined,
2!
148
      },
2✔
149
      select: {
2✔
150
        id: true,
2✔
151
        name: true,
2✔
152
        description: true,
2✔
153
        scopes: true,
2✔
154
        spaceIds: true,
2✔
155
        baseIds: true,
2✔
156
        expiredTime: true,
2✔
157
        lastUsedTime: true,
2✔
158
      },
2✔
159
    });
2✔
160
    return {
2✔
161
      ...this.transformAccessTokenEntity(accessTokenEntity),
2✔
162
      token: getAccessToken(id, sign),
2✔
163
    };
2✔
164
  }
2✔
165

64✔
166
  async updateAccessToken(id: string, updateAccessToken: UpdateAccessTokenRo) {
64✔
167
    const userId = this.cls.get('user.id');
2✔
168
    const { name, description, scopes, spaceIds, baseIds } = updateAccessToken;
2✔
169
    const accessTokenEntity = await this.prismaService.accessToken.update({
2✔
170
      where: { id, userId },
2✔
171
      data: {
2✔
172
        name,
2✔
173
        description,
2✔
174
        scopes: JSON.stringify(scopes),
2✔
175
        spaceIds: spaceIds === null ? null : JSON.stringify(spaceIds),
2!
176
        baseIds: baseIds === null ? null : JSON.stringify(baseIds),
2!
177
      },
2✔
178
      select: {
2✔
179
        id: true,
2✔
180
        name: true,
2✔
181
        description: true,
2✔
182
        scopes: true,
2✔
183
        spaceIds: true,
2✔
184
        baseIds: true,
2✔
185
      },
2✔
186
    });
2✔
187
    return this.transformAccessTokenEntity(accessTokenEntity);
2✔
188
  }
2✔
189

64✔
190
  async getAccessToken(accessTokenId: string) {
64✔
191
    const userId = this.cls.get('user.id');
×
192
    const item = await this.prismaService.accessToken.findFirstOrThrow({
×
193
      where: { userId, id: accessTokenId },
×
194
      select: {
×
195
        id: true,
×
196
        name: true,
×
197
        description: true,
×
198
        scopes: true,
×
199
        spaceIds: true,
×
200
        baseIds: true,
×
201
        createdTime: true,
×
202
        expiredTime: true,
×
203
        lastUsedTime: true,
×
204
      },
×
205
    });
×
206
    return this.transformAccessTokenEntity(item);
×
207
  }
×
208
}
64✔
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