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

teableio / teable / 8389034572

22 Mar 2024 10:38AM UTC coverage: 26.087% (-2.1%) from 28.208%
8389034572

Pull #487

github

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

2100 of 3363 branches covered (62.44%)

282 of 757 new or added lines in 74 files covered. (37.25%)

224 existing lines in 8 files now uncovered.

25574 of 98035 relevant lines covered (26.09%)

5.17 hits per line

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

82.1
/packages/openapi/src/record/get-list.ts
1
import type { RouteConfig } from '@asteasolutions/zod-to-openapi';
1✔
2
import {
1✔
3
  FILTER_DESCRIPTION,
1✔
4
  filterSchema,
1✔
5
  groupSchema,
1✔
6
  IdPrefix,
1✔
7
  recordSchema,
1✔
8
  sortItemSchema,
1✔
9
} from '@teable/core';
1✔
10
import { axios } from '../axios';
1✔
11
import { registerRoute, urlBuilder } from '../utils';
1✔
12
import { z } from '../zod';
1✔
13
import { getRecordQuerySchema } from './get';
1✔
14
import { TQL_README } from './README';
1✔
15

1✔
16
const defaultPageSize = 100;
1✔
17
const maxPageSize = 2000;
1✔
18

1✔
19
export const queryBaseSchema = z.object({
1✔
20
  viewId: z.string().startsWith(IdPrefix.View).optional().openapi({
1✔
21
    example: 'viwXXXXXXX',
1✔
22
    description:
1✔
23
      'Set the view you want to fetch, default is first view. result will filter and sort by view options.',
1✔
24
  }),
1✔
25
  filterByTql: z.string().optional().openapi({
1✔
26
    example: "{field} = 'Completed' AND {field} > 5",
1✔
27
    description: TQL_README,
1✔
28
  }),
1✔
29
  filter: z
1✔
30
    .string()
1✔
31
    .optional()
1✔
32
    .transform((value, ctx) => {
1✔
33
      if (value == null) {
8✔
34
        return value;
8✔
35
      }
8!
NEW
36

×
NEW
37
      const parsingResult = filterSchema.safeParse(JSON.parse(value));
×
NEW
38
      if (!parsingResult.success) {
×
NEW
39
        parsingResult.error.issues.forEach((issue) => {
×
NEW
40
          ctx.addIssue(issue);
×
NEW
41
        });
×
NEW
42
        return z.NEVER;
×
NEW
43
      }
×
NEW
44
      return parsingResult.data;
×
45
    })
1✔
46
    .openapi({
1✔
47
      type: 'string',
1✔
48
      description: FILTER_DESCRIPTION,
1✔
49
    }),
1✔
50
  filterLinkCellCandidate: z
1✔
51
    .tuple([z.string().startsWith(IdPrefix.Field), z.string().startsWith(IdPrefix.Record)])
1✔
52
    .or(z.string().startsWith(IdPrefix.Field))
1✔
53
    .optional()
1✔
54
    .openapi({
1✔
55
      example: ['fldXXXXXXX', 'recXXXXXXX'],
1✔
56
      description:
1✔
57
        'Filter out the records that can be selected by a given link cell from the relational table. For example, if the specified field is one to many or one to one relationship, recordId for which the field has already been selected will not appear.',
1✔
58
    }),
1✔
59
  filterLinkCellSelected: z
1✔
60
    .tuple([z.string().startsWith(IdPrefix.Field), z.string().startsWith(IdPrefix.Record)])
1✔
61
    .or(z.string().startsWith(IdPrefix.Field))
1✔
62
    .optional()
1✔
63
    .openapi({
1✔
64
      example: ['fldXXXXXXX', 'recXXXXXXX'],
1✔
65
      description:
1✔
66
        'Filter out selected records based on this link cell from the relational table. Note that viewId, filter, and orderBy will not take effect in this case because selected records has it own order.',
1✔
67
    }),
1✔
68
});
1✔
69

1✔
70
export type IQueryBaseRo = z.infer<typeof queryBaseSchema>;
1✔
71

1✔
72
const orderByDescription =
1✔
73
  'An array of sort objects that specifies how the records should be ordered.';
1✔
74

1✔
75
export const orderBySchema = sortItemSchema.array().openapi({
1✔
76
  type: 'array',
1✔
77
  description: orderByDescription,
1✔
78
});
1✔
79

1✔
80
// with orderBy for content related fetch
1✔
81
export const contentQueryBaseSchema = queryBaseSchema.extend({
1✔
82
  orderBy: z
1✔
83
    .string()
1✔
84
    .optional()
1✔
85
    .transform((value, ctx) => {
1✔
86
      if (value == null) {
8✔
87
        return value;
8✔
88
      }
8!
NEW
89

×
NEW
90
      const parsingResult = orderBySchema.safeParse(JSON.parse(value));
×
NEW
91
      if (!parsingResult.success) {
×
NEW
92
        parsingResult.error.issues.forEach((issue) => {
×
NEW
93
          ctx.addIssue(issue);
×
NEW
94
        });
×
NEW
95
        return z.NEVER;
×
NEW
96
      }
×
NEW
97
      return parsingResult.data;
×
98
    })
1✔
99
    .openapi({
1✔
100
      type: 'string',
1✔
101
      description: orderByDescription,
1✔
102
    }),
1✔
103
  groupBy: z
1✔
104
    .string()
1✔
105
    .optional()
1✔
106
    .transform((value, ctx) => {
1✔
107
      if (value == null) {
8✔
108
        return value;
8✔
109
      }
8!
NEW
110

×
NEW
111
      const parsingResult = groupSchema.safeParse(JSON.parse(value));
×
NEW
112
      if (!parsingResult.success) {
×
NEW
113
        parsingResult.error.issues.forEach((issue) => {
×
NEW
114
          ctx.addIssue(issue);
×
NEW
115
        });
×
NEW
116
        return z.NEVER;
×
NEW
117
      }
×
NEW
118
      return parsingResult.data;
×
119
    })
1✔
120
    .openapi({
1✔
121
      type: 'string',
1✔
122
      description: 'An array of group objects that specifies how the records should be grouped.',
1✔
123
    }),
1✔
124
});
1✔
125

1✔
126
export const getRecordsRoSchema = getRecordQuerySchema.merge(contentQueryBaseSchema).extend({
1✔
127
  take: z
1✔
128
    .string()
1✔
129
    .or(z.number())
1✔
130
    .transform(Number)
1✔
131
    .pipe(
1✔
132
      z
1✔
133
        .number()
1✔
134
        .min(1, 'You should at least take 1 record')
1✔
135
        .max(maxPageSize, `Can't take more than ${maxPageSize} records, please reduce take count`)
1✔
136
    )
1✔
137
    .default(defaultPageSize)
1✔
138
    .optional()
1✔
139
    .openapi({
1✔
140
      example: defaultPageSize,
1✔
141
      description: `The record count you want to take, maximum is ${maxPageSize}`,
1✔
142
    }),
1✔
143
  skip: z
1✔
144
    .string()
1✔
145
    .or(z.number())
1✔
146
    .transform(Number)
1✔
147
    .pipe(z.number().min(0, 'You can not skip a negative count of records'))
1✔
148
    .default(0)
1✔
149
    .optional()
1✔
150
    .openapi({
1✔
151
      example: 0,
1✔
152
      description: 'The records count you want to skip',
1✔
153
    }),
1✔
154
});
1✔
155

1✔
156
export type IGetRecordsRo = z.infer<typeof getRecordsRoSchema>;
1✔
157

1✔
158
export const recordsSchema = recordSchema.array().openapi({
1✔
159
  example: [
1✔
160
    {
1✔
161
      id: 'recXXXXXXX',
1✔
162
      fields: {
1✔
163
        'single line text': 'text value',
1✔
164
      },
1✔
165
    },
1✔
166
  ],
1✔
167
  description: 'Array of record objects ',
1✔
168
});
1✔
169

1✔
170
export const recordsVoSchema = z.object({
1✔
171
  records: recordSchema.array().openapi({
1✔
172
    example: [
1✔
173
      {
1✔
174
        id: 'recXXXXXXX',
1✔
175
        fields: {
1✔
176
          'single line text': 'text value',
1✔
177
        },
1✔
178
      },
1✔
179
    ],
1✔
180
    description: 'Array of record objects ',
1✔
181
  }),
1✔
182
  offset: z.string().optional().openapi({
1✔
183
    description:
1✔
184
      'If more records exist, the response includes an offset. Use this offset for fetching the next page of records.',
1✔
185
  }),
1✔
186
});
1✔
187

1✔
188
export type IRecordsVo = z.infer<typeof recordsVoSchema>;
1✔
189

1✔
190
export const GET_RECORDS_URL = '/table/{tableId}/record';
1✔
191

1✔
192
export const GetRecordsRoute: RouteConfig = registerRoute({
1✔
193
  method: 'get',
1✔
194
  path: GET_RECORDS_URL,
1✔
195
  description: 'Get multiple records',
1✔
196
  request: {
1✔
197
    params: z.object({
1✔
198
      tableId: z.string(),
1✔
199
    }),
1✔
200
    query: getRecordsRoSchema,
1✔
201
  },
1✔
202
  responses: {
1✔
203
    200: {
1✔
204
      description: 'List of records',
1✔
205
      content: {
1✔
206
        'application/json': {
1✔
207
          schema: recordsVoSchema,
1✔
208
        },
1✔
209
      },
1✔
210
    },
1✔
211
  },
1✔
212
  tags: ['record'],
1✔
213
});
1✔
214

1✔
215
export const getRecords = async (tableId: string, query?: IGetRecordsRo) => {
1✔
216
  return axios.get<IRecordsVo>(
×
217
    urlBuilder(GET_RECORDS_URL, {
×
218
      tableId,
×
219
    }),
×
220
    {
×
221
      params: {
×
222
        ...query,
×
223
        filter: JSON.stringify(query?.filter),
×
224
        orderBy: JSON.stringify(query?.orderBy),
×
225
        groupBy: JSON.stringify(query?.groupBy),
×
226
      },
×
227
    }
×
228
  );
×
229
};
×
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