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

View file

@ -6,6 +6,11 @@ export default {
out: "./drizzle", out: "./drizzle",
schema: "./drizzle/schema.ts", schema: "./drizzle/schema.ts",
dbCredentials: { dbCredentials: {
/* host: "localhost",
port: 40000,
user: "lysand",
password: "lysand",
database: "lysand", */
host: config.database.host, host: config.database.host,
port: Number(config.database.port), port: Number(config.database.port),
user: config.database.username, 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", "version": "5",
"dialect": "pg", "dialect": "pg",
"entries": [ "entries": [
{ {
"idx": 0, "idx": 0,
"version": "5", "version": "5",
"when": 1712805159664, "when": 1712805159664,
"tag": "0000_illegal_living_lightning", "tag": "0000_illegal_living_lightning",
"breakpoints": true "breakpoints": true
}, },
{ {
"idx": 1, "idx": 1,
"version": "5", "version": "5",
"when": 1713055774123, "when": 1713055774123,
"tag": "0001_salty_night_thrasher", "tag": "0001_salty_night_thrasher",
"breakpoints": true "breakpoints": true
}, },
{ {
"idx": 2, "idx": 2,
"version": "5", "version": "5",
"when": 1713056370431, "when": 1713056370431,
"tag": "0002_stiff_ares", "tag": "0002_stiff_ares",
"breakpoints": true "breakpoints": true
}, },
{ {
"idx": 3, "idx": 3,
"version": "5", "version": "5",
"when": 1713056528340, "when": 1713056528340,
"tag": "0003_spicy_arachne", "tag": "0003_spicy_arachne",
"breakpoints": true "breakpoints": true
}, },
{ {
"idx": 4, "idx": 4,
"version": "5", "version": "5",
"when": 1713056712218, "when": 1713056712218,
"tag": "0004_burly_lockjaw", "tag": "0004_burly_lockjaw",
"breakpoints": true "breakpoints": true
}, },
{ {
"idx": 5, "idx": 5,
"version": "5", "version": "5",
"when": 1713056917973, "when": 1713056917973,
"tag": "0005_sleepy_puma", "tag": "0005_sleepy_puma",
"breakpoints": true "breakpoints": true
}, },
{ {
"idx": 6, "idx": 6,
"version": "5", "version": "5",
"when": 1713057159867, "when": 1713057159867,
"tag": "0006_messy_network", "tag": "0006_messy_network",
"breakpoints": true "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(), visibility: text("visibility").notNull(),
inReplyToPostId: uuid("inReplyToPostId"), inReplyToPostId: uuid("inReplyToPostId"),
quotingPostId: uuid("quotingPostId"), quotingPostId: uuid("quotingPostId"),
instanceId: uuid("instanceId").references(() => instance.id, {
onDelete: "cascade",
onUpdate: "cascade",
}),
sensitive: boolean("sensitive").notNull(), sensitive: boolean("sensitive").notNull(),
spoilerText: text("spoiler_text").default("").notNull(), spoilerText: text("spoiler_text").default("").notNull(),
applicationId: uuid("applicationId").references(() => application.id, { applicationId: uuid("applicationId").references(() => application.id, {
@ -683,7 +679,6 @@ export const emojiRelations = relations(emoji, ({ one, many }) => ({
export const instanceRelations = relations(instance, ({ many }) => ({ export const instanceRelations = relations(instance, ({ many }) => ({
users: many(user), users: many(user),
statuses: many(status),
emojis: many(emoji), emojis: many(emoji),
})); }));

View file

@ -1,6 +1,6 @@
import { apiRoute, applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { jsonResponse } from "@response"; 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 { findFirstUser, userToAPI } from "~database/entities/User";
import { db } from "~drizzle/db"; import { db } from "~drizzle/db";
import { instance, status, user } from "~drizzle/schema"; import { instance, status, user } from "~drizzle/schema";
@ -31,7 +31,9 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
count: count(), count: count(),
}) })
.from(status) .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; )[0].count;
const userCount = ( const userCount = (

View file

@ -51,10 +51,6 @@ export default apiRoute<typeof meta, typeof schema>(
), ),
or( or(
eq(status.authorId, user.id), 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 // All statuses where the user is mentioned, using table _StatusToUser which has a: status.id and b: user.id
// WHERE format (... = ...) // WHERE format (... = ...)
sql`EXISTS (SELECT 1 FROM "StatusToMentions" WHERE "StatusToMentions"."statusId" = ${status.id} AND "StatusToMentions"."userId" = ${user.id})`, 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 () => { describe("should paginate properly", async () => {
test("should send correct Link header", async () => { test("should send correct Link header", async () => {
const response = await sendTestRequest( 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, max_id ? lt(status.id, max_id) : undefined,
since_id ? gte(status.id, since_id) : undefined, since_id ? gte(status.id, since_id) : undefined,
min_id ? gt(status.id, min_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 remote
? isNotNull(status.instanceId) ? sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NOT NULL)`
: local : local
? isNull(status.instanceId) ? sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NULL)`
: undefined, : undefined,
only_media only_media
? sql`EXISTS (SELECT 1 FROM "Attachment" WHERE "Attachment"."statusId" = ${status.id})` ? sql`EXISTS (SELECT 1 FROM "Attachment" WHERE "Attachment"."statusId" = ${status.id})`