fix(api): 🐛 Fix incorrect database columns in status queries

Column instanceId of status didn't actually do anything and it was removed
This commit is contained in:
Jesse Wierzbinski 2024-04-15 14:40:40 -10:00
parent 8fc725639c
commit 19f442ec65
No known key found for this signature in database
10 changed files with 1895 additions and 73 deletions

19
cli.ts
View file

@ -8,7 +8,15 @@ import { CliBuilder, CliCommand } from "cli-parser";
import { CliParameterType } from "cli-parser/cli-builder.type";
import Table from "cli-table";
import { config } from "config-manager";
import { type SQL, eq, inArray, isNotNull, isNull, like } from "drizzle-orm";
import {
type SQL,
eq,
inArray,
isNotNull,
isNull,
like,
sql,
} from "drizzle-orm";
import extract from "extract-zip";
import { MediaBackend } from "media-manager";
import { lookup } from "mime-types";
@ -948,16 +956,15 @@ const cliBuilder = new CliBuilder([
return 1;
}
let instanceQuery: SQL<unknown> | undefined = isNull(
status.instanceId,
);
let instanceQuery: SQL<unknown> | undefined =
sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NULL)`;
if (local && remote) {
instanceQuery = undefined;
} else if (local) {
instanceQuery = isNull(status.instanceId);
instanceQuery = sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NULL)`;
} else if (remote) {
instanceQuery = isNotNull(status.instanceId);
instanceQuery = sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NOT NULL)`;
}
const notes = await findManyStatuses({

View file

@ -6,6 +6,11 @@ export default {
out: "./drizzle",
schema: "./drizzle/schema.ts",
dbCredentials: {
/* host: "localhost",
port: 40000,
user: "lysand",
password: "lysand",
database: "lysand", */
host: config.database.host,
port: Number(config.database.port),
user: config.database.username,

View file

@ -0,0 +1,3 @@
ALTER TABLE "Status" DROP CONSTRAINT "Status_instanceId_Instance_id_fk";
--> statement-breakpoint
ALTER TABLE "Status" DROP COLUMN IF EXISTS "instanceId";

File diff suppressed because it is too large Load diff

View file

@ -1,55 +1,62 @@
{
"version": "5",
"dialect": "pg",
"entries": [
{
"idx": 0,
"version": "5",
"when": 1712805159664,
"tag": "0000_illegal_living_lightning",
"breakpoints": true
},
{
"idx": 1,
"version": "5",
"when": 1713055774123,
"tag": "0001_salty_night_thrasher",
"breakpoints": true
},
{
"idx": 2,
"version": "5",
"when": 1713056370431,
"tag": "0002_stiff_ares",
"breakpoints": true
},
{
"idx": 3,
"version": "5",
"when": 1713056528340,
"tag": "0003_spicy_arachne",
"breakpoints": true
},
{
"idx": 4,
"version": "5",
"when": 1713056712218,
"tag": "0004_burly_lockjaw",
"breakpoints": true
},
{
"idx": 5,
"version": "5",
"when": 1713056917973,
"tag": "0005_sleepy_puma",
"breakpoints": true
},
{
"idx": 6,
"version": "5",
"when": 1713057159867,
"tag": "0006_messy_network",
"breakpoints": true
}
]
}
"version": "5",
"dialect": "pg",
"entries": [
{
"idx": 0,
"version": "5",
"when": 1712805159664,
"tag": "0000_illegal_living_lightning",
"breakpoints": true
},
{
"idx": 1,
"version": "5",
"when": 1713055774123,
"tag": "0001_salty_night_thrasher",
"breakpoints": true
},
{
"idx": 2,
"version": "5",
"when": 1713056370431,
"tag": "0002_stiff_ares",
"breakpoints": true
},
{
"idx": 3,
"version": "5",
"when": 1713056528340,
"tag": "0003_spicy_arachne",
"breakpoints": true
},
{
"idx": 4,
"version": "5",
"when": 1713056712218,
"tag": "0004_burly_lockjaw",
"breakpoints": true
},
{
"idx": 5,
"version": "5",
"when": 1713056917973,
"tag": "0005_sleepy_puma",
"breakpoints": true
},
{
"idx": 6,
"version": "5",
"when": 1713057159867,
"tag": "0006_messy_network",
"breakpoints": true
},
{
"idx": 7,
"version": "5",
"when": 1713227918208,
"tag": "0007_naive_sleeper",
"breakpoints": true
}
]
}

View file

@ -223,10 +223,6 @@ export const status = pgTable(
visibility: text("visibility").notNull(),
inReplyToPostId: uuid("inReplyToPostId"),
quotingPostId: uuid("quotingPostId"),
instanceId: uuid("instanceId").references(() => instance.id, {
onDelete: "cascade",
onUpdate: "cascade",
}),
sensitive: boolean("sensitive").notNull(),
spoilerText: text("spoiler_text").default("").notNull(),
applicationId: uuid("applicationId").references(() => application.id, {
@ -683,7 +679,6 @@ export const emojiRelations = relations(emoji, ({ one, many }) => ({
export const instanceRelations = relations(instance, ({ many }) => ({
users: many(user),
statuses: many(status),
emojis: many(emoji),
}));

View file

@ -1,6 +1,6 @@
import { apiRoute, applyConfig } from "@api";
import { jsonResponse } from "@response";
import { and, count, countDistinct, eq, gte, isNull } from "drizzle-orm";
import { and, count, countDistinct, eq, gte, isNull, sql } from "drizzle-orm";
import { findFirstUser, userToAPI } from "~database/entities/User";
import { db } from "~drizzle/db";
import { instance, status, user } from "~drizzle/schema";
@ -31,7 +31,9 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
count: count(),
})
.from(status)
.where(isNull(status.instanceId))
.where(
sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NULL)`,
)
)[0].count;
const userCount = (

View file

@ -51,10 +51,6 @@ export default apiRoute<typeof meta, typeof schema>(
),
or(
eq(status.authorId, user.id),
/* inArray(
status.authorId,
followers.map((f) => f.ownerId),
), */
// All statuses where the user is mentioned, using table _StatusToUser which has a: status.id and b: user.id
// WHERE format (... = ...)
sql`EXISTS (SELECT 1 FROM "StatusToMentions" WHERE "StatusToMentions"."statusId" = ${status.id} AND "StatusToMentions"."userId" = ${user.id})`,

View file

@ -63,6 +63,59 @@ describe(meta.route, () => {
}
});
test("should only fetch local statuses", async () => {
const response = await sendTestRequest(
new Request(
new URL(
`${meta.route}?limit=20&local=true`,
config.http.base_url,
),
{
headers: {
Authorization: `Bearer ${tokens[0].accessToken}`,
},
},
),
);
expect(response.status).toBe(200);
expect(response.headers.get("content-type")).toBe("application/json");
const objects = (await response.json()) as APIStatus[];
expect(objects.length).toBe(20);
for (const [index, status] of objects.entries()) {
expect(status.account).toBeDefined();
expect(status.account.id).toBe(users[0].id);
expect(status.content).toBeDefined();
expect(status.created_at).toBeDefined();
expect(status.id).toBe(timeline[index].id);
}
});
test("should only fetch remote statuses (0)", async () => {
const response = await sendTestRequest(
new Request(
new URL(
`${meta.route}?limit=20&remote=true`,
config.http.base_url,
),
{
headers: {
Authorization: `Bearer ${tokens[0].accessToken}`,
},
},
),
);
expect(response.status).toBe(200);
expect(response.headers.get("content-type")).toBe("application/json");
const objects = (await response.json()) as APIStatus[];
expect(objects.length).toBe(0);
});
describe("should paginate properly", async () => {
test("should send correct Link header", async () => {
const response = await sendTestRequest(

View file

@ -50,10 +50,11 @@ export default apiRoute<typeof meta, typeof schema>(
max_id ? lt(status.id, max_id) : undefined,
since_id ? gte(status.id, since_id) : undefined,
min_id ? gt(status.id, min_id) : undefined,
// use authorId to grab user, then use user.instanceId to filter local/remote statuses
remote
? isNotNull(status.instanceId)
? sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NOT NULL)`
: local
? isNull(status.instanceId)
? sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NULL)`
: undefined,
only_media
? sql`EXISTS (SELECT 1 FROM "Attachment" WHERE "Attachment"."statusId" = ${status.id})`