1
1
import { v4 as uuidv4 } from "uuid"
2
2
import { AppDataSource } from "data-source"
3
- import { ApiTrace , ApiEndpoint , DataField , Alert } from "models"
3
+ import { ApiTrace , ApiEndpoint , DataField , Alert , OpenApiSpec } from "models"
4
4
import { DataFieldService } from "services/data-field"
5
5
import { SpecService } from "services/spec"
6
6
import { AlertService } from "services/alert"
7
- import { DatabaseService } from "services/database"
8
7
import { RedisClient } from "utils/redis"
9
8
import { TRACES_QUEUE } from "~/constants"
10
9
import { QueryRunner } from "typeorm"
@@ -18,14 +17,20 @@ import {
18
17
import { getPathTokens } from "@common/utils"
19
18
import { AlertType } from "@common/enums"
20
19
import { isGraphQlEndpoint } from "services/graphql"
20
+ import { isQueryFailedError , retryTypeormTransaction } from "utils/db"
21
+ import { MetloContext } from "types"
22
+ import { DatabaseService } from "services/database"
23
+ import { getEntityManager , getQB } from "services/database/utils"
21
24
22
- const GET_ENDPOINT_QUERY = `
25
+ const getEndpointQuery = ( ctx : MetloContext ) => `
23
26
SELECT
24
27
endpoint. *,
25
28
CASE WHEN spec."isAutoGenerated" IS NULL THEN NULL ELSE json_build_object('isAutoGenerated', spec."isAutoGenerated") END as "openapiSpec"
26
29
FROM
27
- "api_endpoint" endpoint
28
- LEFT JOIN "open_api_spec" spec ON endpoint."openapiSpecName" = spec.name
30
+ ${ ApiEndpoint . getTableName ( ctx ) } endpoint
31
+ LEFT JOIN ${ OpenApiSpec . getTableName (
32
+ ctx ,
33
+ ) } spec ON endpoint."openapiSpecName" = spec.name
29
34
WHERE
30
35
$1 ~ "pathRegex"
31
36
AND method = $2
39
44
1
40
45
`
41
46
42
- const GET_DATA_FIELDS_QUERY = `
47
+ const getDataFieldsQuery = ( ctx : MetloContext ) => `
43
48
SELECT
44
49
uuid,
45
50
"dataClasses"::text[],
@@ -53,21 +58,27 @@ SELECT
53
58
"dataPath",
54
59
"apiEndpointUuid"
55
60
FROM
56
- data_field
61
+ ${ DataField . getTableName ( ctx ) } data_field
57
62
WHERE
58
63
"apiEndpointUuid" = $1
59
64
`
60
65
61
- const getQueuedApiTrace = async ( ) : Promise < QueuedApiTrace > => {
66
+ const getQueuedApiTrace = async (
67
+ ctx : MetloContext ,
68
+ ) : Promise < QueuedApiTrace > => {
62
69
try {
63
- const traceString = await RedisClient . popValueFromRedisList ( TRACES_QUEUE )
70
+ const traceString = await RedisClient . popValueFromRedisList (
71
+ ctx ,
72
+ TRACES_QUEUE ,
73
+ )
64
74
return JSON . parse ( traceString )
65
75
} catch ( err ) {
66
76
return null
67
77
}
68
78
}
69
79
70
80
const analyze = async (
81
+ ctx : MetloContext ,
71
82
trace : QueuedApiTrace ,
72
83
apiEndpoint : ApiEndpoint ,
73
84
queryRunner : QueryRunner ,
@@ -76,11 +87,13 @@ const analyze = async (
76
87
endpointUpdateDates ( trace . createdAt , apiEndpoint )
77
88
const dataFields = DataFieldService . findAllDataFields ( trace , apiEndpoint )
78
89
let alerts = await SpecService . findOpenApiSpecDiff (
90
+ ctx ,
79
91
trace ,
80
92
apiEndpoint ,
81
93
queryRunner ,
82
94
)
83
95
const sensitiveDataAlerts = await AlertService . createDataFieldAlerts (
96
+ ctx ,
84
97
dataFields ,
85
98
apiEndpoint . uuid ,
86
99
apiEndpoint . path ,
@@ -90,6 +103,7 @@ const analyze = async (
90
103
alerts = alerts ?. concat ( sensitiveDataAlerts )
91
104
if ( newEndpoint ) {
92
105
const newEndpointAlert = await AlertService . createAlert (
106
+ ctx ,
93
107
AlertType . NEW_ENDPOINT ,
94
108
apiEndpoint ,
95
109
)
@@ -99,18 +113,17 @@ const analyze = async (
99
113
}
100
114
101
115
await queryRunner . startTransaction ( )
102
- await DatabaseService . retryTypeormTransaction (
116
+ await retryTypeormTransaction (
103
117
( ) =>
104
- queryRunner . manager . insert ( ApiTrace , {
118
+ getEntityManager ( ctx , queryRunner ) . insert ( ApiTrace , {
105
119
...trace ,
106
120
apiEndpointUuid : apiEndpoint . uuid ,
107
121
} ) ,
108
122
5 ,
109
123
)
110
- await DatabaseService . retryTypeormTransaction (
124
+ await retryTypeormTransaction (
111
125
( ) =>
112
- queryRunner . manager
113
- . createQueryBuilder ( )
126
+ getQB ( ctx , queryRunner )
114
127
. insert ( )
115
128
. into ( DataField )
116
129
. values ( dataFields )
@@ -127,21 +140,19 @@ const analyze = async (
127
140
. execute ( ) ,
128
141
5 ,
129
142
)
130
- await DatabaseService . retryTypeormTransaction (
143
+ await retryTypeormTransaction (
131
144
( ) =>
132
- queryRunner . manager
133
- . createQueryBuilder ( )
145
+ getQB ( ctx , queryRunner )
134
146
. insert ( )
135
147
. into ( Alert )
136
148
. values ( alerts )
137
149
. orIgnore ( )
138
150
. execute ( ) ,
139
151
5 ,
140
152
)
141
- await DatabaseService . retryTypeormTransaction (
153
+ await retryTypeormTransaction (
142
154
( ) =>
143
- queryRunner . manager
144
- . createQueryBuilder ( )
155
+ getQB ( ctx , queryRunner )
145
156
. update ( ApiEndpoint )
146
157
. set ( {
147
158
firstDetected : apiEndpoint . firstDetected ,
@@ -156,6 +167,7 @@ const analyze = async (
156
167
}
157
168
158
169
const generateEndpoint = async (
170
+ ctx : MetloContext ,
159
171
trace : QueuedApiTrace ,
160
172
queryRunner : QueryRunner ,
161
173
) : Promise < void > => {
@@ -201,36 +213,35 @@ const generateEndpoint = async (
201
213
202
214
try {
203
215
await queryRunner . startTransaction ( )
204
- await DatabaseService . retryTypeormTransaction (
216
+ await retryTypeormTransaction (
205
217
( ) =>
206
- queryRunner . manager
207
- . createQueryBuilder ( )
218
+ getQB ( ctx , queryRunner )
208
219
. insert ( )
209
220
. into ( ApiEndpoint )
210
221
. values ( apiEndpoint )
211
222
. execute ( ) ,
212
223
5 ,
213
224
)
214
225
await queryRunner . commitTransaction ( )
215
- await analyze ( trace , apiEndpoint , queryRunner , true )
226
+ await analyze ( ctx , trace , apiEndpoint , queryRunner , true )
216
227
} catch ( err ) {
217
228
if ( queryRunner . isTransactionActive ) {
218
229
await queryRunner . rollbackTransaction ( )
219
230
}
220
- if ( DatabaseService . isQueryFailedError ( err ) && err . code === "23505" ) {
221
- const existingEndpoint = await queryRunner . manager . findOne (
222
- ApiEndpoint ,
223
- {
224
- where : {
225
- path : trace . path ,
226
- host : trace . host ,
227
- method : trace . method ,
228
- } ,
229
- relations : { dataFields : true } ,
231
+ if ( isQueryFailedError ( err ) && err . code === "23505" ) {
232
+ const existingEndpoint = await getEntityManager (
233
+ ctx ,
234
+ queryRunner ,
235
+ ) . findOne ( ApiEndpoint , {
236
+ where : {
237
+ path : trace . path ,
238
+ host : trace . host ,
239
+ method : trace . method ,
230
240
} ,
231
- )
241
+ relations : { dataFields : true } ,
242
+ } )
232
243
if ( existingEndpoint ) {
233
- await analyze ( trace , existingEndpoint , queryRunner )
244
+ await analyze ( ctx , trace , existingEndpoint , queryRunner )
234
245
}
235
246
} else {
236
247
console . error ( `Error generating new endpoint: ${ err } ` )
@@ -240,6 +251,8 @@ const generateEndpoint = async (
240
251
}
241
252
242
253
const analyzeTraces = async ( ) : Promise < void > => {
254
+ const ctx : MetloContext = { }
255
+
243
256
const datasource = await AppDataSource . initialize ( )
244
257
if ( ! datasource . isInitialized ) {
245
258
console . error ( "Couldn't initialize datasource..." )
@@ -251,26 +264,26 @@ const analyzeTraces = async (): Promise<void> => {
251
264
await queryRunner . connect ( )
252
265
while ( true ) {
253
266
try {
254
- const trace = await getQueuedApiTrace ( )
267
+ const trace = await getQueuedApiTrace ( ctx )
255
268
if ( trace ) {
256
269
trace . createdAt = new Date ( trace . createdAt )
257
270
const apiEndpoint : ApiEndpoint = (
258
- await queryRunner . query ( GET_ENDPOINT_QUERY , [
271
+ await queryRunner . query ( getEndpointQuery ( ctx ) , [
259
272
trace . path ,
260
273
trace . method ,
261
274
trace . host ,
262
275
] )
263
276
) ?. [ 0 ]
264
277
if ( apiEndpoint && ! skipAutoGeneratedMatch ( apiEndpoint , trace . path ) ) {
265
- const dataFields : DataField [ ] = await queryRunner . query (
266
- GET_DATA_FIELDS_QUERY ,
278
+ const dataFields : DataField [ ] = await DatabaseService . executeRawQuery (
279
+ getDataFieldsQuery ( ctx ) ,
267
280
[ apiEndpoint . uuid ] ,
268
281
)
269
282
apiEndpoint . dataFields = dataFields
270
- await analyze ( trace , apiEndpoint , queryRunner )
283
+ await analyze ( ctx , trace , apiEndpoint , queryRunner )
271
284
} else {
272
285
if ( trace . responseStatus !== 404 && trace . responseStatus !== 405 ) {
273
- await generateEndpoint ( trace , queryRunner )
286
+ await generateEndpoint ( ctx , trace , queryRunner )
274
287
}
275
288
}
276
289
}
0 commit comments