refactor: 🏷️ Move all types that represent ORM abstractions to ORM class static properties

This commit is contained in:
Jesse Wierzbinski 2024-11-04 14:58:17 +01:00
parent ca31830fb3
commit 02c3c9d0bf
No known key found for this signature in database
17 changed files with 143 additions and 136 deletions

View file

@ -12,7 +12,7 @@ import {
import { z } from "zod"; import { z } from "zod";
import { BaseInterface } from "./base.ts"; import { BaseInterface } from "./base.ts";
export type ApplicationType = InferSelectModel<typeof Applications>; type ApplicationType = InferSelectModel<typeof Applications>;
export class Application extends BaseInterface<typeof Applications> { export class Application extends BaseInterface<typeof Applications> {
public static schema: z.ZodType<APIApplication> = z.object({ public static schema: z.ZodType<APIApplication> = z.object({
@ -23,6 +23,8 @@ export class Application extends BaseInterface<typeof Applications> {
scopes: z.string().optional(), scopes: z.string().optional(),
}); });
public static $type: ApplicationType;
public async reload(): Promise<void> { public async reload(): Promise<void> {
const reloaded = await Application.fromId(this.data.id); const reloaded = await Application.fromId(this.data.id);

View file

@ -16,7 +16,7 @@ import { MediaBackendType } from "~/packages/config-manager/config.type";
import { config } from "~/packages/config-manager/index.ts"; import { config } from "~/packages/config-manager/index.ts";
import { BaseInterface } from "./base.ts"; import { BaseInterface } from "./base.ts";
export type AttachmentType = InferSelectModel<typeof Attachments>; type AttachmentType = InferSelectModel<typeof Attachments>;
export class Attachment extends BaseInterface<typeof Attachments> { export class Attachment extends BaseInterface<typeof Attachments> {
public static schema: z.ZodType<ApiAttachment> = z.object({ public static schema: z.ZodType<ApiAttachment> = z.object({
@ -47,6 +47,8 @@ export class Attachment extends BaseInterface<typeof Attachments> {
blurhash: z.string().nullable(), blurhash: z.string().nullable(),
}); });
public static $type: AttachmentType;
public async reload(): Promise<void> { public async reload(): Promise<void> {
const reloaded = await Attachment.fromId(this.data.id); const reloaded = await Attachment.fromId(this.data.id);

View file

@ -17,7 +17,7 @@ import { z } from "zod";
import { BaseInterface } from "./base.ts"; import { BaseInterface } from "./base.ts";
import { Instance } from "./instance.ts"; import { Instance } from "./instance.ts";
export type EmojiWithInstance = InferSelectModel<typeof Emojis> & { type EmojiWithInstance = InferSelectModel<typeof Emojis> & {
instance: InferSelectModel<typeof Instances> | null; instance: InferSelectModel<typeof Instances> | null;
}; };
@ -30,6 +30,8 @@ export class Emoji extends BaseInterface<typeof Emojis, EmojiWithInstance> {
static_url: z.string(), static_url: z.string(),
}); });
public static $type: EmojiWithInstance;
public async reload(): Promise<void> { public async reload(): Promise<void> {
const reloaded = await Emoji.fromId(this.data.id); const reloaded = await Emoji.fromId(this.data.id);

View file

@ -20,9 +20,11 @@ import { config } from "~/packages/config-manager/index.ts";
import { BaseInterface } from "./base.ts"; import { BaseInterface } from "./base.ts";
import { User } from "./user.ts"; import { User } from "./user.ts";
export type InstanceType = InferSelectModel<typeof Instances>; type InstanceType = InferSelectModel<typeof Instances>;
export class Instance extends BaseInterface<typeof Instances> { export class Instance extends BaseInterface<typeof Instances> {
public static $type: InstanceType;
public async reload(): Promise<void> { public async reload(): Promise<void> {
const reloaded = await Instance.fromId(this.data.id); const reloaded = await Instance.fromId(this.data.id);

View file

@ -1,7 +1,12 @@
import { RolePermission } from "@versia/client/types"; import { RolePermission } from "@versia/client/types";
import type { Delete, LikeExtension } from "@versia/federation/types"; import type { Delete, LikeExtension } from "@versia/federation/types";
import { db } from "@versia/kit/db"; import { db } from "@versia/kit/db";
import { Likes, Notifications } from "@versia/kit/tables"; import {
Likes,
type Notes,
Notifications,
type Users,
} from "@versia/kit/tables";
import { import {
type InferInsertModel, type InferInsertModel,
type InferSelectModel, type InferSelectModel,
@ -13,15 +18,13 @@ import {
} from "drizzle-orm"; } from "drizzle-orm";
import { z } from "zod"; import { z } from "zod";
import { config } from "~/packages/config-manager/index.ts"; import { config } from "~/packages/config-manager/index.ts";
import type { Status } from "../functions/status.ts";
import type { UserType } from "../functions/user.ts";
import { BaseInterface } from "./base.ts"; import { BaseInterface } from "./base.ts";
import { Note } from "./note.ts"; import { Note } from "./note.ts";
import { User } from "./user.ts"; import { User } from "./user.ts";
export type LikeType = InferSelectModel<typeof Likes> & { type LikeType = InferSelectModel<typeof Likes> & {
liker: UserType; liker: InferSelectModel<typeof Users>;
liked: Status; liked: InferSelectModel<typeof Notes>;
}; };
export class Like extends BaseInterface<typeof Likes, LikeType> { export class Like extends BaseInterface<typeof Likes, LikeType> {
@ -35,6 +38,8 @@ export class Like extends BaseInterface<typeof Likes, LikeType> {
icon: z.string().nullable(), icon: z.string().nullable(),
}); });
public static $type: LikeType;
public async reload(): Promise<void> { public async reload(): Promise<void> {
const reloaded = await Like.fromId(this.data.id); const reloaded = await Like.fromId(this.data.id);

View file

@ -14,7 +14,7 @@ import type {
Delete as VersiaDelete, Delete as VersiaDelete,
Note as VersiaNote, Note as VersiaNote,
} from "@versia/federation/types"; } from "@versia/federation/types";
import { Notification, db } from "@versia/kit/db"; import { type Instance, Notification, db } from "@versia/kit/db";
import { import {
Attachments, Attachments,
EmojiToNote, EmojiToNote,
@ -24,6 +24,7 @@ import {
} from "@versia/kit/tables"; } from "@versia/kit/tables";
import { import {
type InferInsertModel, type InferInsertModel,
type InferSelectModel,
type SQL, type SQL,
and, and,
desc, desc,
@ -36,7 +37,6 @@ import { htmlToText } from "html-to-text";
import { createRegExp, exactly, global } from "magic-regexp"; import { createRegExp, exactly, global } from "magic-regexp";
import { z } from "zod"; import { z } from "zod";
import { import {
type StatusWithRelations,
contentToHtml, contentToHtml,
findManyNotes, findManyNotes,
parseTextMentions, parseTextMentions,
@ -48,10 +48,37 @@ import { BaseInterface } from "./base.ts";
import { Emoji } from "./emoji.ts"; import { Emoji } from "./emoji.ts";
import { User } from "./user.ts"; import { User } from "./user.ts";
type NoteType = InferSelectModel<typeof Notes>;
type NoteTypeWithRelations = NoteType & {
author: typeof User.$type;
mentions: (InferSelectModel<typeof Users> & {
instance: typeof Instance.$type | null;
})[];
attachments: (typeof Attachment.$type)[];
reblog: NoteTypeWithoutRecursiveRelations | null;
emojis: (typeof Emoji.$type)[];
reply: NoteType | null;
quote: NoteType | null;
application: typeof Application.$type | null;
reblogCount: number;
likeCount: number;
replyCount: number;
pinned: boolean;
reblogged: boolean;
muted: boolean;
liked: boolean;
};
export type NoteTypeWithoutRecursiveRelations = Omit<
NoteTypeWithRelations,
"reply" | "quote" | "reblog"
>;
/** /**
* Gives helpers to fetch notes from database in a nice format * Gives helpers to fetch notes from database in a nice format
*/ */
export class Note extends BaseInterface<typeof Notes, StatusWithRelations> { export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
public static schema: z.ZodType<ApiStatus> = z.object({ public static schema: z.ZodType<ApiStatus> = z.object({
id: z.string().uuid(), id: z.string().uuid(),
uri: z.string().url(), uri: z.string().url(),
@ -142,7 +169,9 @@ export class Note extends BaseInterface<typeof Notes, StatusWithRelations> {
bookmarked: z.boolean(), bookmarked: z.boolean(),
}); });
public save(): Promise<StatusWithRelations> { public static $type: NoteTypeWithRelations;
public save(): Promise<NoteTypeWithRelations> {
return this.update(this.data); return this.update(this.data);
} }
@ -814,8 +843,8 @@ export class Note extends BaseInterface<typeof Notes, StatusWithRelations> {
} }
public async update( public async update(
newStatus: Partial<StatusWithRelations>, newStatus: Partial<NoteTypeWithRelations>,
): Promise<StatusWithRelations> { ): Promise<NoteTypeWithRelations> {
await db.update(Notes).set(newStatus).where(eq(Notes.id, this.data.id)); await db.update(Notes).set(newStatus).where(eq(Notes.id, this.data.id));
const updated = await Note.fromId(this.data.id); const updated = await Note.fromId(this.data.id);
@ -932,7 +961,7 @@ export class Note extends BaseInterface<typeof Notes, StatusWithRelations> {
// TODO: Add polls // TODO: Add polls
poll: null, poll: null,
reblog: data.reblog reblog: data.reblog
? await new Note(data.reblog as StatusWithRelations).toApi( ? await new Note(data.reblog as NoteTypeWithRelations).toApi(
userFetching, userFetching,
) )
: null, : null,

View file

@ -12,9 +12,7 @@ import {
import { z } from "zod"; import { z } from "zod";
import { MediaBackendType } from "~/packages/config-manager/config.type"; import { MediaBackendType } from "~/packages/config-manager/config.type";
import { config } from "~/packages/config-manager/index.ts"; import { config } from "~/packages/config-manager/index.ts";
import type { StatusWithRelations } from "../functions/status.ts";
import { import {
type UserWithRelations,
transformOutputToUserWithRelations, transformOutputToUserWithRelations,
userExtrasTemplate, userExtrasTemplate,
userRelations, userRelations,
@ -22,8 +20,8 @@ import {
import { BaseInterface } from "./base.ts"; import { BaseInterface } from "./base.ts";
export type NotificationType = InferSelectModel<typeof Notifications> & { export type NotificationType = InferSelectModel<typeof Notifications> & {
status: StatusWithRelations | null; status: typeof Note.$type | null;
account: UserWithRelations; account: typeof User.$type;
}; };
export class Notification extends BaseInterface< export class Notification extends BaseInterface<

View file

@ -14,9 +14,9 @@ import { z } from "zod";
import { BaseInterface } from "./base.ts"; import { BaseInterface } from "./base.ts";
import type { User } from "./user.ts"; import type { User } from "./user.ts";
export type RelationshipType = InferSelectModel<typeof Relationships>; type RelationshipType = InferSelectModel<typeof Relationships>;
export type RelationshipWithOpposite = RelationshipType & { type RelationshipWithOpposite = RelationshipType & {
followedBy: boolean; followedBy: boolean;
blockedBy: boolean; blockedBy: boolean;
requestedBy: boolean; requestedBy: boolean;
@ -43,6 +43,8 @@ export class Relationship extends BaseInterface<
showing_reblogs: z.boolean(), showing_reblogs: z.boolean(),
}); });
public static $type: RelationshipWithOpposite;
public async reload(): Promise<void> { public async reload(): Promise<void> {
const reloaded = await Relationship.fromId(this.data.id); const reloaded = await Relationship.fromId(this.data.id);

View file

@ -18,7 +18,7 @@ import { z } from "zod";
import { config } from "~/packages/config-manager/index.ts"; import { config } from "~/packages/config-manager/index.ts";
import { BaseInterface } from "./base.ts"; import { BaseInterface } from "./base.ts";
export type RoleType = InferSelectModel<typeof Roles>; type RoleType = InferSelectModel<typeof Roles>;
export class Role extends BaseInterface<typeof Roles> { export class Role extends BaseInterface<typeof Roles> {
public static schema = z.object({ public static schema = z.object({
@ -31,6 +31,8 @@ export class Role extends BaseInterface<typeof Roles> {
icon: z.string().nullable(), icon: z.string().nullable(),
}); });
public static $type: RoleType;
public async reload(): Promise<void> { public async reload(): Promise<void> {
const reloaded = await Role.fromId(this.data.id); const reloaded = await Role.fromId(this.data.id);

View file

@ -1,6 +1,6 @@
import type { Token as ApiToken } from "@versia/client/types"; import type { Token as ApiToken } from "@versia/client/types";
import { User, db } from "@versia/kit/db"; import { type Application, User, db } from "@versia/kit/db";
import { type Applications, Tokens } from "@versia/kit/tables"; import { Tokens } from "@versia/kit/tables";
import { import {
type InferInsertModel, type InferInsertModel,
type InferSelectModel, type InferSelectModel,
@ -12,8 +12,8 @@ import {
import { z } from "zod"; import { z } from "zod";
import { BaseInterface } from "./base.ts"; import { BaseInterface } from "./base.ts";
export type TokenType = InferSelectModel<typeof Tokens> & { type TokenType = InferSelectModel<typeof Tokens> & {
application: InferSelectModel<typeof Applications> | null; application: typeof Application.$type | null;
}; };
export class Token extends BaseInterface<typeof Tokens, TokenType> { export class Token extends BaseInterface<typeof Tokens, TokenType> {
@ -24,6 +24,8 @@ export class Token extends BaseInterface<typeof Tokens, TokenType> {
created_at: z.number(), created_at: z.number(),
}); });
public static $type: TokenType;
public async reload(): Promise<void> { public async reload(): Promise<void> {
const reloaded = await Token.fromId(this.data.id); const reloaded = await Token.fromId(this.data.id);

View file

@ -33,6 +33,7 @@ import {
import chalk from "chalk"; import chalk from "chalk";
import { import {
type InferInsertModel, type InferInsertModel,
type InferSelectModel,
type SQL, type SQL,
and, and,
countDistinct, countDistinct,
@ -47,7 +48,6 @@ import {
import { htmlToText } from "html-to-text"; import { htmlToText } from "html-to-text";
import { z } from "zod"; import { z } from "zod";
import { import {
type UserWithRelations,
findManyUsers, findManyUsers,
followAcceptToVersia, followAcceptToVersia,
followRejectToVersia, followRejectToVersia,
@ -64,6 +64,18 @@ import type { Note } from "./note.ts";
import { Relationship } from "./relationship.ts"; import { Relationship } from "./relationship.ts";
import { Role } from "./role.ts"; import { Role } from "./role.ts";
type UserWithInstance = InferSelectModel<typeof Users> & {
instance: typeof Instance.$type | null;
};
type UserWithRelations = UserWithInstance & {
emojis: (typeof Emoji.$type)[];
followerCount: number;
followingCount: number;
statusCount: number;
roles: (typeof Role.$type)[];
};
/** /**
* Gives helpers to fetch users from database in a nice format * Gives helpers to fetch users from database in a nice format
*/ */
@ -125,6 +137,8 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
mute_expires_at: z.string().optional(), mute_expires_at: z.string().optional(),
}); });
public static $type: UserWithRelations;
public async reload(): Promise<void> { public async reload(): Promise<void> {
const reloaded = await User.fromId(this.data.id); const reloaded = await User.fromId(this.data.id);

View file

@ -2,22 +2,9 @@ import { mentionValidator } from "@/api";
import { sanitizeHtml, sanitizeHtmlInline } from "@/sanitization"; import { sanitizeHtml, sanitizeHtmlInline } from "@/sanitization";
import markdownItTaskLists from "@hackmd/markdown-it-task-lists"; import markdownItTaskLists from "@hackmd/markdown-it-task-lists";
import type { ContentFormat } from "@versia/federation/types"; import type { ContentFormat } from "@versia/federation/types";
import { User, db } from "@versia/kit/db"; import { type Note, User, db } from "@versia/kit/db";
import { import { Instances, Users } from "@versia/kit/tables";
type Attachments, import { and, eq, inArray, isNull, or, sql } from "drizzle-orm";
Instances,
type Notes,
Users,
} from "@versia/kit/tables";
import {
type InferSelectModel,
and,
eq,
inArray,
isNull,
or,
sql,
} from "drizzle-orm";
import linkifyHtml from "linkify-html"; import linkifyHtml from "linkify-html";
import { import {
anyOf, anyOf,
@ -31,42 +18,13 @@ import {
import MarkdownIt from "markdown-it"; import MarkdownIt from "markdown-it";
import markdownItContainer from "markdown-it-container"; import markdownItContainer from "markdown-it-container";
import markdownItTocDoneRight from "markdown-it-toc-done-right"; import markdownItTocDoneRight from "markdown-it-toc-done-right";
import type { ApplicationType } from "~/classes/database/application.ts";
import type { EmojiWithInstance } from "~/classes/database/emoji.ts";
import { config } from "~/packages/config-manager/index.ts"; import { config } from "~/packages/config-manager/index.ts";
import { import {
type UserWithInstance,
type UserWithRelations,
transformOutputToUserWithRelations, transformOutputToUserWithRelations,
userExtrasTemplate, userExtrasTemplate,
userRelations, userRelations,
} from "./user.ts"; } from "./user.ts";
export type Status = InferSelectModel<typeof Notes>;
export type StatusWithRelations = Status & {
author: UserWithRelations;
mentions: UserWithInstance[];
attachments: InferSelectModel<typeof Attachments>[];
reblog: StatusWithoutRecursiveRelations | null;
emojis: EmojiWithInstance[];
reply: Status | null;
quote: Status | null;
application: ApplicationType | null;
reblogCount: number;
likeCount: number;
replyCount: number;
pinned: boolean;
reblogged: boolean;
muted: boolean;
liked: boolean;
};
export type StatusWithoutRecursiveRelations = Omit<
StatusWithRelations,
"reply" | "quote" | "reblog"
>;
/** /**
* Wrapper against the Status object to make it easier to work with * Wrapper against the Status object to make it easier to work with
* @param query * @param query
@ -75,7 +33,7 @@ export type StatusWithoutRecursiveRelations = Omit<
export const findManyNotes = async ( export const findManyNotes = async (
query: Parameters<typeof db.query.Notes.findMany>[0], query: Parameters<typeof db.query.Notes.findMany>[0],
userId?: string, userId?: string,
): Promise<StatusWithRelations[]> => { ): Promise<(typeof Note.$type)[]> => {
const output = await db.query.Notes.findMany({ const output = await db.query.Notes.findMany({
...query, ...query,
with: { with: {

View file

@ -3,25 +3,17 @@ import type {
FollowAccept, FollowAccept,
FollowReject, FollowReject,
} from "@versia/federation/types"; } from "@versia/federation/types";
import { type Application, type Token, type User, db } from "@versia/kit/db"; import {
import type { Instances, Roles, Users } from "@versia/kit/tables"; type Application,
type Emoji,
type Instance,
type Role,
type Token,
type User,
db,
} from "@versia/kit/db";
import type { Users } from "@versia/kit/tables";
import { type InferSelectModel, type SQL, sql } from "drizzle-orm"; import { type InferSelectModel, type SQL, sql } from "drizzle-orm";
import type { EmojiWithInstance } from "~/classes/database/emoji.ts";
export type UserType = InferSelectModel<typeof Users>;
export type UserWithInstance = UserType & {
instance: InferSelectModel<typeof Instances> | null;
};
export type UserWithRelations = UserType & {
instance: InferSelectModel<typeof Instances> | null;
emojis: EmojiWithInstance[];
followerCount: number;
followingCount: number;
statusCount: number;
roles: InferSelectModel<typeof Roles>[];
};
export const userRelations = { export const userRelations = {
instance: true, instance: true,
@ -84,24 +76,24 @@ export interface AuthData {
} }
export const transformOutputToUserWithRelations = ( export const transformOutputToUserWithRelations = (
user: Omit<UserType, "endpoints"> & { user: Omit<InferSelectModel<typeof Users>, "endpoints"> & {
followerCount: unknown; followerCount: unknown;
followingCount: unknown; followingCount: unknown;
statusCount: unknown; statusCount: unknown;
emojis: { emojis: {
userId: string; userId: string;
emojiId: string; emojiId: string;
emoji?: EmojiWithInstance; emoji?: typeof Emoji.$type;
}[]; }[];
instance: InferSelectModel<typeof Instances> | null; instance: typeof Instance.$type | null;
roles: { roles: {
userId: string; userId: string;
roleId: string; roleId: string;
role?: InferSelectModel<typeof Roles>; role?: typeof Role.$type;
}[]; }[];
endpoints: unknown; endpoints: unknown;
}, },
): UserWithRelations => { ): typeof User.$type => {
return { return {
...user, ...user,
followerCount: Number(user.followerCount), followerCount: Number(user.followerCount),
@ -121,17 +113,17 @@ export const transformOutputToUserWithRelations = (
emojis: user.emojis.map( emojis: user.emojis.map(
(emoji) => (emoji) =>
(emoji as unknown as Record<string, object>) (emoji as unknown as Record<string, object>)
.emoji as EmojiWithInstance, .emoji as typeof Emoji.$type,
), ),
roles: user.roles roles: user.roles
.map((role) => role.role) .map((role) => role.role)
.filter(Boolean) as InferSelectModel<typeof Roles>[], .filter(Boolean) as (typeof Role.$type)[],
}; };
}; };
export const findManyUsers = async ( export const findManyUsers = async (
query: Parameters<typeof db.query.Users.findMany>[0], query: Parameters<typeof db.query.Users.findMany>[0],
): Promise<UserWithRelations[]> => { ): Promise<(typeof User.$type)[]> => {
const output = await db.query.Users.findMany({ const output = await db.query.Users.findMany({
...query, ...query,
with: { with: {

View file

@ -1,15 +1,9 @@
import { parseUserAddress, userAddressValidator } from "@/api"; import { parseUserAddress, userAddressValidator } from "@/api";
import { Args, type Command, Flags, type Interfaces } from "@oclif/core"; import { Args, type Command, Flags, type Interfaces } from "@oclif/core";
import { Instance, User, db } from "@versia/kit/db"; import { type Emoji, Instance, User, db } from "@versia/kit/db";
import { Emojis, Instances, Users } from "@versia/kit/tables"; import { Emojis, Instances, Users } from "@versia/kit/tables";
import chalk from "chalk"; import chalk from "chalk";
import { import { and, eq, getTableColumns, like } from "drizzle-orm";
type InferSelectModel,
and,
eq,
getTableColumns,
like,
} from "drizzle-orm";
import { BaseCommand } from "./base.ts"; import { BaseCommand } from "./base.ts";
export type FlagsType<T extends typeof Command> = Interfaces.InferredFlags< export type FlagsType<T extends typeof Command> = Interfaces.InferredFlags<
@ -210,9 +204,12 @@ export abstract class EmojiFinderCommand<
} }
public async findEmojis(): Promise< public async findEmojis(): Promise<
(InferSelectModel<typeof Emojis> & { Omit<
typeof Emoji.$type & {
instanceUrl: string | null; instanceUrl: string | null;
})[] },
"instance"
>[]
> { > {
// Check if there are asterisks in the identifier but no pattern flag, warn the user if so // Check if there are asterisks in the identifier but no pattern flag, warn the user if so
if (this.args.identifier.includes("*") && !this.flags.pattern) { if (this.args.identifier.includes("*") && !this.flags.pattern) {

View file

@ -1,6 +1,6 @@
import { db } from "@versia/kit/db"; import { type Application, db } from "@versia/kit/db";
import type { InferSelectModel, SQL } from "@versia/kit/drizzle"; import type { InferSelectModel, SQL } from "@versia/kit/drizzle";
import type { Applications, OpenIdLoginFlows } from "@versia/kit/tables"; import type { OpenIdLoginFlows } from "@versia/kit/tables";
import { import {
type AuthorizationResponseError, type AuthorizationResponseError,
type AuthorizationServer, type AuthorizationServer,
@ -18,7 +18,6 @@ import {
userInfoRequest, userInfoRequest,
validateAuthResponse, validateAuthResponse,
} from "oauth4webapi"; } from "oauth4webapi";
import type { ApplicationType } from "~/classes/database/application";
export const oauthDiscoveryRequest = ( export const oauthDiscoveryRequest = (
issuerUrl: string | URL, issuerUrl: string | URL,
@ -37,7 +36,7 @@ const getFlow = (
flowId: string, flowId: string,
): Promise< ): Promise<
| (InferSelectModel<typeof OpenIdLoginFlows> & { | (InferSelectModel<typeof OpenIdLoginFlows> & {
application?: ApplicationType | null; application?: typeof Application.$type | null;
}) })
| undefined | undefined
> => { > => {
@ -143,7 +142,7 @@ export const automaticOidcFlow = async (
message: string, message: string,
flow: flow:
| (InferSelectModel<typeof OpenIdLoginFlows> & { | (InferSelectModel<typeof OpenIdLoginFlows> & {
application?: InferSelectModel<typeof Applications> | null; application?: typeof Application.$type | null;
}) })
| null, | null,
) => Response, ) => Response,
@ -152,7 +151,7 @@ export const automaticOidcFlow = async (
| { | {
userInfo: UserInfoResponse; userInfo: UserInfoResponse;
flow: InferSelectModel<typeof OpenIdLoginFlows> & { flow: InferSelectModel<typeof OpenIdLoginFlows> & {
application?: ApplicationType | null; application?: typeof Application.$type | null;
}; };
claims: Record<string, unknown>; claims: Record<string, unknown>;
} }

View file

@ -1,11 +1,11 @@
import { describe, expect, it } from "bun:test"; import { describe, expect, it } from "bun:test";
import { checkIfOauthIsValid } from "@/oauth"; import { checkIfOauthIsValid } from "@/oauth";
import { Application } from "@versia/kit/db"; import { Application } from "@versia/kit/db";
import type { ApplicationType } from "~/classes/database/application";
describe("checkIfOauthIsValid", () => { describe("checkIfOauthIsValid", () => {
it("should return true when routeScopes and application.scopes are empty", () => { it("should return true when routeScopes and application.scopes are empty", () => {
const application = new Application({ scopes: "" } as ApplicationType); const application = new Application({
scopes: "",
} as typeof Application.$type);
const routeScopes: string[] = []; const routeScopes: string[] = [];
const result = checkIfOauthIsValid(application, routeScopes); const result = checkIfOauthIsValid(application, routeScopes);
expect(result).toBe(true); expect(result).toBe(true);
@ -14,7 +14,7 @@ describe("checkIfOauthIsValid", () => {
it("should return true when routeScopes is empty and application.scopes contains write:* or write", () => { it("should return true when routeScopes is empty and application.scopes contains write:* or write", () => {
const application = new Application({ const application = new Application({
scopes: "write:*", scopes: "write:*",
} as ApplicationType); } as typeof Application.$type);
const routeScopes: string[] = []; const routeScopes: string[] = [];
const result = checkIfOauthIsValid(application, routeScopes); const result = checkIfOauthIsValid(application, routeScopes);
expect(result).toBe(true); expect(result).toBe(true);
@ -23,7 +23,7 @@ describe("checkIfOauthIsValid", () => {
it("should return true when routeScopes is empty and application.scopes contains read:* or read", () => { it("should return true when routeScopes is empty and application.scopes contains read:* or read", () => {
const application = new Application({ const application = new Application({
scopes: "read:*", scopes: "read:*",
} as ApplicationType); } as typeof Application.$type);
const routeScopes: string[] = []; const routeScopes: string[] = [];
const result = checkIfOauthIsValid(application, routeScopes); const result = checkIfOauthIsValid(application, routeScopes);
expect(result).toBe(true); expect(result).toBe(true);
@ -32,7 +32,7 @@ describe("checkIfOauthIsValid", () => {
it("should return true when routeScopes contains only write: permissions and application.scopes contains write:* or write", () => { it("should return true when routeScopes contains only write: permissions and application.scopes contains write:* or write", () => {
const application = new Application({ const application = new Application({
scopes: "write:*", scopes: "write:*",
} as ApplicationType); } as typeof Application.$type);
const routeScopes = ["write:users", "write:posts"]; const routeScopes = ["write:users", "write:posts"];
const result = checkIfOauthIsValid(application, routeScopes); const result = checkIfOauthIsValid(application, routeScopes);
expect(result).toBe(true); expect(result).toBe(true);
@ -41,7 +41,7 @@ describe("checkIfOauthIsValid", () => {
it("should return true when routeScopes contains only read: permissions and application.scopes contains read:* or read", () => { it("should return true when routeScopes contains only read: permissions and application.scopes contains read:* or read", () => {
const application = new Application({ const application = new Application({
scopes: "read:*", scopes: "read:*",
} as ApplicationType); } as typeof Application.$type);
const routeScopes = ["read:users", "read:posts"]; const routeScopes = ["read:users", "read:posts"];
const result = checkIfOauthIsValid(application, routeScopes); const result = checkIfOauthIsValid(application, routeScopes);
expect(result).toBe(true); expect(result).toBe(true);
@ -50,7 +50,7 @@ describe("checkIfOauthIsValid", () => {
it("should return true when routeScopes contains both write: and read: permissions and application.scopes contains write:* or write and read:* or read", () => { it("should return true when routeScopes contains both write: and read: permissions and application.scopes contains write:* or write and read:* or read", () => {
const application = new Application({ const application = new Application({
scopes: "write:* read:*", scopes: "write:* read:*",
} as ApplicationType); } as typeof Application.$type);
const routeScopes = ["write:users", "read:posts"]; const routeScopes = ["write:users", "read:posts"];
const result = checkIfOauthIsValid(application, routeScopes); const result = checkIfOauthIsValid(application, routeScopes);
expect(result).toBe(true); expect(result).toBe(true);
@ -59,7 +59,7 @@ describe("checkIfOauthIsValid", () => {
it("should return false when routeScopes contains write: permissions but application.scopes does not contain write:* or write", () => { it("should return false when routeScopes contains write: permissions but application.scopes does not contain write:* or write", () => {
const application = new Application({ const application = new Application({
scopes: "read:*", scopes: "read:*",
} as ApplicationType); } as typeof Application.$type);
const routeScopes = ["write:users", "write:posts"]; const routeScopes = ["write:users", "write:posts"];
const result = checkIfOauthIsValid(application, routeScopes); const result = checkIfOauthIsValid(application, routeScopes);
expect(result).toBe(false); expect(result).toBe(false);
@ -68,14 +68,16 @@ describe("checkIfOauthIsValid", () => {
it("should return false when routeScopes contains read: permissions but application.scopes does not contain read:* or read", () => { it("should return false when routeScopes contains read: permissions but application.scopes does not contain read:* or read", () => {
const application = new Application({ const application = new Application({
scopes: "write:*", scopes: "write:*",
} as ApplicationType); } as typeof Application.$type);
const routeScopes = ["read:users", "read:posts"]; const routeScopes = ["read:users", "read:posts"];
const result = checkIfOauthIsValid(application, routeScopes); const result = checkIfOauthIsValid(application, routeScopes);
expect(result).toBe(false); expect(result).toBe(false);
}); });
it("should return false when routeScopes contains both write: and read: permissions but application.scopes does not contain write:* or write and read:* or read", () => { it("should return false when routeScopes contains both write: and read: permissions but application.scopes does not contain write:* or write and read:* or read", () => {
const application = new Application({ scopes: "" } as ApplicationType); const application = new Application({
scopes: "",
} as typeof Application.$type);
const routeScopes = ["write:users", "read:posts"]; const routeScopes = ["write:users", "read:posts"];
const result = checkIfOauthIsValid(application, routeScopes); const result = checkIfOauthIsValid(application, routeScopes);
expect(result).toBe(false); expect(result).toBe(false);
@ -84,7 +86,7 @@ describe("checkIfOauthIsValid", () => {
it("should return true when routeScopes contains a mix of valid and invalid permissions and application.scopes contains all the required permissions", () => { it("should return true when routeScopes contains a mix of valid and invalid permissions and application.scopes contains all the required permissions", () => {
const application = new Application({ const application = new Application({
scopes: "write:* read:*", scopes: "write:* read:*",
} as ApplicationType); } as typeof Application.$type);
const routeScopes = ["write:users", "invalid:permission", "read:posts"]; const routeScopes = ["write:users", "invalid:permission", "read:posts"];
const result = checkIfOauthIsValid(application, routeScopes); const result = checkIfOauthIsValid(application, routeScopes);
expect(result).toBe(true); expect(result).toBe(true);
@ -93,7 +95,7 @@ describe("checkIfOauthIsValid", () => {
it("should return false when routeScopes contains a mix of valid and invalid permissions but application.scopes does not contain all the required permissions", () => { it("should return false when routeScopes contains a mix of valid and invalid permissions but application.scopes does not contain all the required permissions", () => {
const application = new Application({ const application = new Application({
scopes: "write:*", scopes: "write:*",
} as ApplicationType); } as typeof Application.$type);
const routeScopes = ["write:users", "invalid:permission", "read:posts"]; const routeScopes = ["write:users", "invalid:permission", "read:posts"];
const result = checkIfOauthIsValid(application, routeScopes); const result = checkIfOauthIsValid(application, routeScopes);
expect(result).toBe(false); expect(result).toBe(false);
@ -102,7 +104,7 @@ describe("checkIfOauthIsValid", () => {
it("should return true when routeScopes contains a mix of valid write and read permissions and application.scopes contains all the required permissions", () => { it("should return true when routeScopes contains a mix of valid write and read permissions and application.scopes contains all the required permissions", () => {
const application = new Application({ const application = new Application({
scopes: "write:* read:posts", scopes: "write:* read:posts",
} as ApplicationType); } as typeof Application.$type);
const routeScopes = ["write:users", "read:posts"]; const routeScopes = ["write:users", "read:posts"];
const result = checkIfOauthIsValid(application, routeScopes); const result = checkIfOauthIsValid(application, routeScopes);
expect(result).toBe(true); expect(result).toBe(true);
@ -111,7 +113,7 @@ describe("checkIfOauthIsValid", () => {
it("should return false when routeScopes contains a mix of valid write and read permissions but application.scopes does not contain all the required permissions", () => { it("should return false when routeScopes contains a mix of valid write and read permissions but application.scopes does not contain all the required permissions", () => {
const application = new Application({ const application = new Application({
scopes: "write:*", scopes: "write:*",
} as ApplicationType); } as typeof Application.$type);
const routeScopes = ["write:users", "read:posts"]; const routeScopes = ["write:users", "read:posts"];
const result = checkIfOauthIsValid(application, routeScopes); const result = checkIfOauthIsValid(application, routeScopes);
expect(result).toBe(false); expect(result).toBe(false);

View file

@ -3,9 +3,8 @@ import { randomString } from "@/math";
import { Note, Token, User, db } from "@versia/kit/db"; import { Note, Token, User, db } from "@versia/kit/db";
import { Notes, Users } from "@versia/kit/tables"; import { Notes, Users } from "@versia/kit/tables";
import { solveChallenge } from "altcha-lib"; import { solveChallenge } from "altcha-lib";
import { asc, inArray, like } from "drizzle-orm"; import { type InferSelectModel, asc, inArray, like } from "drizzle-orm";
import { appFactory } from "~/app"; import { appFactory } from "~/app";
import type { Status } from "~/classes/functions/status";
import { searchManager } from "~/classes/search/search-manager"; import { searchManager } from "~/classes/search/search-manager";
import { setupDatabase } from "~/drizzle/db"; import { setupDatabase } from "~/drizzle/db";
import { config } from "~/packages/config-manager"; import { config } from "~/packages/config-manager";
@ -92,7 +91,7 @@ export const getTestUsers = async (
export const getTestStatuses = async ( export const getTestStatuses = async (
count: number, count: number,
user: User, user: User,
partial?: Partial<Status>, partial?: Partial<InferSelectModel<typeof Notes>>,
): Promise<Note["data"][]> => { ): Promise<Note["data"][]> => {
const statuses: Note[] = []; const statuses: Note[] = [];