Skip to content

Commit 7049f96

Browse files
authored
fix: optimize endpoint page (metlo-labs#68)
1 parent e49c8a1 commit 7049f96

File tree

16 files changed

+314
-362
lines changed

16 files changed

+314
-362
lines changed

backend/src/data-source.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { addUniqueConstraintApiEndpoint1666678487137 } from "migrations/16666784
2222
import { dropAnalyzedColumnFromApiTrace1666752646836 } from "migrations/1666752646836-drop-analyzed-column-from-api-trace"
2323
import { addIndexForDataField1666941075032 } from "migrations/1666941075032-add-index-for-data-field"
2424
import { addIsgraphqlColumnApiEndpoint1667095325334 } from "migrations/1667095325334-add-isgraphql-column-api-endpoint"
25+
import { addApiEndpointUuidIndexForAlert1667259254414 } from "migrations/1667259254414-add-apiEndpointUuid-index-for-alert"
2526

2627
export const AppDataSource: DataSource = new DataSource({
2728
type: "postgres",
@@ -49,6 +50,7 @@ export const AppDataSource: DataSource = new DataSource({
4950
dropAnalyzedColumnFromApiTrace1666752646836,
5051
addIndexForDataField1666941075032,
5152
addIsgraphqlColumnApiEndpoint1667095325334,
53+
addApiEndpointUuidIndexForAlert1667259254414,
5254
],
5355
migrationsRun: runMigration,
5456
logging: false,

backend/src/jobs.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ import {
99
} from "services/jobs"
1010
import runAllTests from "services/testing/runAllTests"
1111
import { logAggregatedStats } from "services/logging"
12+
import { DateTime } from "luxon"
13+
14+
const log = (logMessage: string, newLine?: boolean) =>
15+
console.log(
16+
`${newLine ? "\n" : ""}${DateTime.utc().toString()} ${logMessage}`,
17+
)
1218

1319
const main = async () => {
1420
const datasource = await AppDataSource.initialize()
@@ -25,63 +31,63 @@ const main = async () => {
2531
const logAggregateStatsSem = semaphore(1)
2632
const checkForUnauthenticatedSem = semaphore(1)
2733

28-
schedule.scheduleJob("30 * * * *", () => {
34+
schedule.scheduleJob("*/10 * * * *", () => {
2935
generateSpecSem.take(async () => {
30-
console.log("\nGenerating OpenAPI Spec Files...")
36+
log("Generating OpenAPI Spec Files...", true)
3137
await generateOpenApiSpec()
32-
console.log("Finished generating OpenAPI Spec Files.")
38+
log("Finished generating OpenAPI Spec Files.")
3339
generateSpecSem.leave()
3440
})
3541
})
3642

3743
schedule.scheduleJob("30 * * * * ", () => {
3844
checkForUnauthenticatedSem.take(async () => {
39-
console.log("\nChecking for Unauthenticated Endpoints")
45+
log("Checking for Unauthenticated Endpoints", true)
4046
await checkForUnauthenticatedEndpoints()
41-
console.log("Finished checking for Unauthenticated Endpoints")
47+
log("Finished checking for Unauthenticated Endpoints")
4248
checkForUnauthenticatedSem.leave()
4349
})
4450
})
4551

4652
// Offset by 15 minutes past every 4th hour, so that there isn't any excess database slowdown
4753
schedule.scheduleJob("15 * * * *", () => {
4854
unsecuredAlertsSem.take(async () => {
49-
console.log("\nGenerating Alerts for Unsecured Endpoints")
55+
log("Generating Alerts for Unsecured Endpoints", true)
5056
await monitorEndpointForHSTS()
51-
console.log("Finished generating alerts for Unsecured Endpoints.")
57+
log("Finished generating alerts for Unsecured Endpoints.")
5258
unsecuredAlertsSem.leave()
5359
})
5460
})
5561

5662
schedule.scheduleJob("30 * * * *", () => {
5763
testsSem.take(async () => {
58-
console.log("\nRunning Tests...")
64+
log("Running Tests...", true)
5965
await runAllTests()
60-
console.log("Finished running tests.")
66+
log("Finished running tests.")
6167
testsSem.leave()
6268
})
6369
})
6470

6571
schedule.scheduleJob("*/10 * * * *", () => {
6672
clearApiTracesSem.take(async () => {
67-
console.log("\nClearing Api Trace data...")
73+
log("Clearing Api Trace data...", true)
6874
await clearApiTraces()
69-
console.log("Finished clearing Api Trace data.")
75+
log("Finished clearing Api Trace data.")
7076
clearApiTracesSem.leave()
7177
})
7278
})
7379

7480
if ((process.env.DISABLE_LOGGING_STATS || "false").toLowerCase() == "false") {
7581
schedule.scheduleJob("0 */6 * * *", () => {
7682
logAggregateStatsSem.take(async () => {
77-
console.log("\nLogging Aggregated Stats...")
83+
log("Logging Aggregated Stats...", true)
7884
await logAggregatedStats()
79-
console.log("Finished Logging Aggregated Stats.")
85+
log("Finished Logging Aggregated Stats.")
8086
logAggregateStatsSem.leave()
8187
})
8288
})
8389
} else {
84-
console.log("\nLogging Aggregated Stats Disabled...")
90+
log("Logging Aggregated Stats Disabled...", true)
8591
}
8692

8793
process.on("SIGINT", () => {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { MigrationInterface, QueryRunner } from "typeorm"
2+
3+
export class addApiEndpointUuidIndexForAlert1667259254414
4+
implements MigrationInterface
5+
{
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
await queryRunner.query(
8+
`CREATE INDEX IF NOT EXISTS "apiEndpointUuid_alert" ON "alert" ("apiEndpointUuid")`,
9+
)
10+
}
11+
12+
public async down(queryRunner: QueryRunner): Promise<void> {
13+
await queryRunner.query(`DROP INDEX IF EXISTS "apiEndpointUuid_alert"`)
14+
}
15+
}

backend/src/models/alert.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
Column,
44
CreateDateColumn,
55
Entity,
6+
Index,
67
ManyToOne,
78
PrimaryGeneratedColumn,
89
UpdateDateColumn,
@@ -27,6 +28,7 @@ export class Alert extends BaseEntity {
2728
riskScore: RiskScore
2829

2930
@Column()
31+
@Index("apiEndpointUuid_alert")
3032
apiEndpointUuid: string
3133

3234
@ManyToOne(() => ApiEndpoint, apiEndpoint => apiEndpoint.alerts)

backend/src/services/get-endpoints/index.ts

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import {
44
ApiEndpointTest,
55
ApiTrace,
66
AggregateTraceDataHourly,
7+
Alert,
8+
DataField,
9+
OpenApiSpec,
710
} from "models"
811
import {
912
GetEndpointParams,
@@ -17,6 +20,28 @@ import Error404NotFound from "errors/error-404-not-found"
1720
import { getRiskScore } from "utils"
1821
import { getEndpointsCountQuery, getEndpointsQuery } from "./queries"
1922

23+
const GET_DATA_FIELDS_QUERY = `
24+
SELECT
25+
uuid,
26+
"dataClasses"::text[],
27+
"falsePositives"::text[],
28+
"scannerIdentified"::text[],
29+
"dataType",
30+
"dataTag",
31+
"dataSection",
32+
"createdAt",
33+
"updatedAt",
34+
"dataPath",
35+
"apiEndpointUuid"
36+
FROM
37+
data_field
38+
WHERE
39+
"apiEndpointUuid" = $1
40+
ORDER BY
41+
"dataTag" ASC,
42+
"dataPath" ASC
43+
`
44+
2045
export class GetEndpointsService {
2146
static async updateIsAuthenticated(
2247
apiEndpointUuid: string,
@@ -118,29 +143,29 @@ export class GetEndpointsService {
118143
const queryRunner = AppDataSource.createQueryRunner()
119144
try {
120145
await queryRunner.connect()
121-
const endpoint = await queryRunner.manager.findOne(ApiEndpoint, {
122-
select: {
123-
alerts: {
124-
uuid: true,
125-
status: true,
126-
},
127-
},
128-
where: { uuid: endpointId },
129-
relations: {
130-
dataFields: true,
131-
openapiSpec: true,
132-
alerts: true,
133-
},
134-
order: {
135-
dataFields: {
136-
dataTag: "ASC",
137-
dataPath: "ASC",
138-
},
139-
},
140-
})
146+
const endpoint = await queryRunner.manager
147+
.createQueryBuilder()
148+
.from(ApiEndpoint, "endpoint")
149+
.where("uuid = :id", { id: endpointId })
150+
.getRawOne()
141151
if (!endpoint) {
142152
throw new Error404NotFound("Endpoint does not exist.")
143153
}
154+
const alerts = await queryRunner.manager
155+
.createQueryBuilder()
156+
.select(["uuid", "status"])
157+
.from(Alert, "alert")
158+
.where(`"apiEndpointUuid" = :id`, { id: endpointId })
159+
.getRawMany()
160+
const dataFields: DataField[] = await queryRunner.query(
161+
GET_DATA_FIELDS_QUERY,
162+
[endpointId],
163+
)
164+
const openapiSpec = await queryRunner.manager
165+
.createQueryBuilder()
166+
.from(OpenApiSpec, "spec")
167+
.where("name = :name", { name: endpoint.openapiSpecName })
168+
.getRawOne()
144169
const traces = await queryRunner.manager.find(ApiTrace, {
145170
where: { apiEndpointUuid: endpoint.uuid },
146171
order: { createdAt: "DESC" },
@@ -151,6 +176,9 @@ export class GetEndpointsService {
151176
})
152177
return {
153178
...endpoint,
179+
alerts,
180+
dataFields,
181+
openapiSpec,
154182
traces: [...traces],
155183
tests: tests as Array<Test>,
156184
}

0 commit comments

Comments
 (0)