Skip to content

Commit 4b32319

Browse files
committed
more stat apis
1 parent 6c3be96 commit 4b32319

File tree

4 files changed

+156
-2
lines changed

4 files changed

+156
-2
lines changed

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "api",
3-
"version": "1.1.3",
3+
"version": "1.2.0",
44
"scripts": {
55
"build": "rm -rf lib && esbuild `find src \\( -name '*.ts' -o -name '*.tsx' \\)` --platform='node' --sourcemap --ignore-annotations --format='cjs' --target='es2022' --outdir='lib' && esbuild src/index.ts --platform='node' --sourcemap --ignore-annotations --format='cjs' --target='es2022' --outdir='lib' --banner:js='require(\"module-alias\").addAlias(\"@\", __dirname);'",
66
"kit": "drizzle-kit",

Diff for: src/api/routes/global/extensions/{id}/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { globalAPIRouter } from "@/api"
22
import { time } from "@rjweb/utils"
3-
import { and, eq, or, sql } from "drizzle-orm"
3+
import { and, eq, or } from "drizzle-orm"
44

55
export = new globalAPIRouter.Path('/')
66
.http('GET', '/', (http) => http

Diff for: src/api/routes/global/extensions/{id}/versions.ts

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { globalAPIRouter } from "@/api"
2+
import { time } from "@rjweb/utils"
3+
import { and, desc, eq, inArray, or, sql } from "drizzle-orm"
4+
5+
export = new globalAPIRouter.Path('/')
6+
.http('GET', '/', (http) => http
7+
.document({
8+
description: 'Get the install-base of a blueprint extensions versions',
9+
responses: {
10+
200: {
11+
description: 'Success',
12+
content: {
13+
'application/json': {
14+
schema: {
15+
type: 'object',
16+
additionalProperties: {
17+
type: 'number'
18+
}
19+
}
20+
}
21+
}
22+
}
23+
}, parameters: [
24+
{
25+
name: 'id',
26+
in: 'path',
27+
description: 'The ID or identifier of the extension',
28+
required: true,
29+
schema: {
30+
anyOf: [
31+
{ type: 'integer' },
32+
{ type: 'string' }
33+
]
34+
}
35+
}
36+
]
37+
})
38+
.onRequest(async(ctr) => {
39+
const id = ctr.params.get('id', '')
40+
if (!id) return ctr.status(ctr.$status.BAD_REQUEST).print({ errors: ['Invalid ID'] })
41+
42+
const idInt = parseInt(id)
43+
44+
const [ extension ] = await ctr["@"].cache.use(`extension::${id}`, () => ctr["@"].database.select(ctr["@"].database.fields.extension)
45+
.from(ctr["@"].database.schema.extensions)
46+
.innerJoin(ctr["@"].database.schema.authors, eq(ctr["@"].database.schema.extensions.authorId, ctr["@"].database.schema.authors.id))
47+
.where(and(
48+
eq(ctr["@"].database.schema.extensions.hidden, false),
49+
or(
50+
!isNaN(idInt) ? eq(ctr["@"].database.schema.extensions.id, idInt) : undefined,
51+
eq(ctr["@"].database.schema.extensions.identifier, id)
52+
)
53+
)),
54+
time(5).m()
55+
)
56+
57+
if (!extension) return ctr.status(ctr.$status.NOT_FOUND).print({ errors: ['Extension not found'] })
58+
59+
const versionStats = await ctr["@"].cache.use(`versions::${extension.id}`, () => ctr["@"].database.select({
60+
version: sql<string>`ext->>'version'`.as('version'),
61+
percentage: sql<number>`
62+
(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER ())::numeric(5,2)
63+
`.mapWith(Number).as('percentage')
64+
})
65+
.from(
66+
ctr["@"].database.select({
67+
ext: sql`jsonb_array_elements(data->'blueprint'->'extensions')`.as('ext')
68+
})
69+
.from(ctr["@"].database.schema.telemetryData)
70+
.where(and(
71+
inArray(
72+
ctr["@"].database.schema.telemetryData.id,
73+
ctr["@"].database.select({ id: ctr["@"].database.schema.telemetryPanelsWithLatest.latest.latestTelemetryDataId })
74+
.from(ctr["@"].database.schema.telemetryPanelsWithLatest)
75+
),
76+
sql`created > NOW() - INTERVAL '2 days'`
77+
))
78+
.as('subq')
79+
)
80+
.where(sql`ext->>'identifier' = ${extension.identifier}`)
81+
.groupBy(sql`ext->>'version'`)
82+
.orderBy(desc(sql`ext->>'version'`)),
83+
time(5).m()
84+
)
85+
86+
return ctr.print(Object.fromEntries(versionStats.map((stat) => [
87+
stat.version,
88+
stat.percentage
89+
])))
90+
})
91+
)

Diff for: src/api/routes/global/stats/flags.ts

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { globalAPIRouter } from "@/api"
2+
import { number, time } from "@rjweb/utils"
3+
import { and, inArray, sql } from "drizzle-orm"
4+
5+
export = new globalAPIRouter.Path('/')
6+
.http('GET', '/', (http) => http
7+
.document({
8+
description: 'Get the share of blueprint flags in percentage',
9+
responses: {
10+
200: {
11+
description: 'Success',
12+
content: {
13+
'application/json': {
14+
schema: {
15+
type: 'object',
16+
additionalProperties: {
17+
type: 'number'
18+
}
19+
}
20+
}
21+
}
22+
}
23+
}
24+
})
25+
.onRequest(async(ctr) => {
26+
const flagStats = await ctr["@"].cache.use('flags::all', () => ctr["@"].database.select({
27+
flagName: sql<string>`flag.key`.as('flagName'),
28+
percentageEnabled: sql<number>`
29+
(COUNT(*) * 100.0 / (
30+
SELECT COUNT(*)
31+
FROM ${ctr["@"].database.schema.telemetryData}
32+
WHERE id IN (
33+
SELECT ${ctr["@"].database.schema.telemetryPanelsWithLatest.latest.latestTelemetryDataId}
34+
FROM ${ctr["@"].database.schema.telemetryPanelsWithLatest}
35+
)
36+
AND created > NOW() - INTERVAL '2 days'
37+
))::numeric(5,2)
38+
`.mapWith(Number).as('percentageEnabled')
39+
})
40+
.from(ctr["@"].database.schema.telemetryData)
41+
.leftJoin(sql`LATERAL jsonb_each(data->'blueprint'->'flags') AS flag(key, value)`, sql`true`)
42+
.where(and(
43+
inArray(
44+
ctr["@"].database.schema.telemetryData.id,
45+
ctr["@"].database.select({ id: ctr["@"].database.schema.telemetryPanelsWithLatest.latest.latestTelemetryDataId })
46+
.from(ctr["@"].database.schema.telemetryPanelsWithLatest)
47+
),
48+
sql`created > NOW() - INTERVAL '2 days'`,
49+
sql`flag.value = 'true'`
50+
))
51+
.groupBy(sql`flag.key`),
52+
time(5).m()
53+
)
54+
55+
return ctr.print(Object.fromEntries(flagStats.map((stat) => [
56+
stat.flagName,
57+
{
58+
enabled: stat.percentageEnabled,
59+
disabled: 100 - stat.percentageEnabled
60+
}
61+
])))
62+
})
63+
)

0 commit comments

Comments
 (0)