From 36103fbca045f80d1fc4929ac931c47909d44497 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 21 Jun 2022 17:42:09 -0400 Subject: [PATCH] Add option types (#67) * Testing some option types over the API. * v0.17.0-rc.6 * Removing a few to better test. * v0.17.0-rc.7 * Simpler again. * v0.17.0-rc.8 * Trying swan.io boxed * v0.17.0-rc.13 * Trying option-t * v0.17.0-rc.14 * Adding rust-style Options using @sniptt/monads - Also added serialization with class-transformer * v0.17.0-rc.15 * Trying to fix encodeGetParams. * v0.17.0-rc.16 * Trying to fix encodeGetParams 2. * v0.17.0-rc.17 * Using init constructors. * v0.17.0-rc.18 * v0.17.0-rc.19 * Add more type info. * v0.17.0-rc.20 * Adding reflect-metadata * v0.17.0-rc.21 * Adding type to siteview.site * v0.17.0-rc.22 * Adding rest of nested types. * v0.17.0-rc.23 * Adding toOption function. * v0.17.0-rc.24 * Trying to fix send. * v0.17.0-rc.25 * Try to stringify jsonres. * v0.17.0-rc.26 * Try to stringify jsonres 2. * v0.17.0-rc.27 * Adding toOption function. * v0.17.0-rc.28 * Forgot to type a communityview. * v0.17.0-rc.29 * Forgot to type a registrationapplicationview. * v0.17.0-rc.30 --- package.json | 5 +- src/http.ts | 675 ++++++++++++++++++++++---------- src/index.ts | 1 + src/interfaces/api/comment.ts | 167 ++++++-- src/interfaces/api/community.ts | 215 ++++++++-- src/interfaces/api/person.ts | 450 ++++++++++++++++----- src/interfaces/api/post.ts | 225 +++++++++-- src/interfaces/api/site.ts | 427 ++++++++++++++++---- src/interfaces/others.ts | 65 ++- src/interfaces/source.ts | 323 +++++++++++---- src/interfaces/views.ts | 189 +++++++-- src/utils.ts | 15 + src/websocket.ts | 24 +- tsconfig.json | 1 + yarn.lock | 15 + 15 files changed, 2146 insertions(+), 651 deletions(-) create mode 100644 src/utils.ts diff --git a/package.json b/package.json index d65f115..0c69332 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "lemmy-js-client", "description": "A javascript / typescript client for Lemmy", - "version": "0.16.4-rc.3", + "version": "0.17.0-rc.30", "author": "Dessalines ", "license": "AGPL-3.0", "main": "./dist/index.js", @@ -16,10 +16,12 @@ }, "repository": "https://github.com/LemmyNet/lemmy-js-client", "devDependencies": { + "@sniptt/monads": "^0.5.10", "@types/node": "^17.0.33", "@types/node-fetch": "^3.0.3", "@typescript-eslint/eslint-plugin": "^5.23.0", "@typescript-eslint/parser": "^5.23.0", + "class-transformer": "^0.5.1", "eslint": "^8.15.0", "eslint-plugin-prettier": "^4.0.0", "husky": "^8.0.1", @@ -29,6 +31,7 @@ "prettier-plugin-import-sort": "^0.0.7", "prettier-plugin-organize-imports": "^2.3.4", "prettier-plugin-packagejson": "^2.2.18", + "reflect-metadata": "^0.1.13", "sortpack": "^2.2.0", "typedoc": "^0.21.6", "typedoc-plugin-sourcefile-url": "^1.0.6", diff --git a/src/http.ts b/src/http.ts index 82a337c..487d931 100644 --- a/src/http.ts +++ b/src/http.ts @@ -1,3 +1,4 @@ +import { ClassConstructor, deserialize, serialize } from "class-transformer"; import fetch from "node-fetch"; import { CommentReportResponse, @@ -154,649 +155,904 @@ export class LemmyHttp { /** * Gets the site, and your user data. + * * `HTTP.GET /site` */ - async getSite(form: GetSite): Promise { - return this.wrapper(HttpType.Get, "/site", form); + async getSite(form: GetSite) { + return this.wrapper(HttpType.Get, "/site", form, GetSiteResponse); } /** * Create your site. + * * `HTTP.POST /site` */ - async createSite(form: CreateSite): Promise { - return this.wrapper(HttpType.Post, "/site", form); + async createSite(form: CreateSite) { + return this.wrapper(HttpType.Post, "/site", form, SiteResponse); } /** * Edit your site. + * * `HTTP.PUT /site` */ - async editSite(form: EditSite): Promise { - return this.wrapper(HttpType.Put, "/site", form); + async editSite(form: EditSite) { + return this.wrapper(HttpType.Put, "/site", form, SiteResponse); } /** * Leave the Site admins. + * * `HTTP.POST /user/leave_admin` */ - async leaveAdmin(form: LeaveAdmin): Promise { - return this.wrapper(HttpType.Post, "/user/leave_admin", form); + async leaveAdmin(form: LeaveAdmin) { + return this.wrapper( + HttpType.Post, + "/user/leave_admin", + form, + GetSiteResponse + ); } /** * Get your site configuration. + * * `HTTP.GET /site/config` */ - async getSiteConfig(form: GetSiteConfig): Promise { - return this.wrapper(HttpType.Get, "/site/config", form); + async getSiteConfig(form: GetSiteConfig) { + return this.wrapper( + HttpType.Get, + "/site/config", + form, + GetSiteConfigResponse + ); } /** * Save your site config. + * * `HTTP.PUT /site/config` */ - async saveSiteConfig(form: SaveSiteConfig): Promise { - return this.wrapper(HttpType.Put, "/site/config", form); + async saveSiteConfig(form: SaveSiteConfig) { + return this.wrapper( + HttpType.Put, + "/site/config", + form, + GetSiteConfigResponse + ); } /** * Get the modlog. + * * `HTTP.GET /modlog` */ - async getModlog(form: GetModlog): Promise { - return this.wrapper(HttpType.Get, "/modlog", form); + async getModlog(form: GetModlog) { + return this.wrapper(HttpType.Get, "/modlog", form, GetModlogResponse); } /** * Search lemmy. + * * `HTTP.GET /search` */ - async search(form: Search): Promise { - return this.wrapper(HttpType.Get, "/search", form); + async search(form: Search) { + return this.wrapper(HttpType.Get, "/search", form, SearchResponse); } /** * Fetch a non-local / federated object. + * * `HTTP.GET /resolve_object` */ - async resolveObject(form: ResolveObject): Promise { - return this.wrapper(HttpType.Get, "/resolve_object", form); + async resolveObject(form: ResolveObject) { + return this.wrapper( + HttpType.Get, + "/resolve_object", + form, + ResolveObjectResponse + ); } /** * Create a new community. + * * `HTTP.POST /community` */ - async createCommunity(form: CreateCommunity): Promise { - return this.wrapper(HttpType.Post, "/community", form); + async createCommunity(form: CreateCommunity) { + return this.wrapper(HttpType.Post, "/community", form, CommunityResponse); } /** * Get / fetch a community. + * * `HTTP.GET /community` */ - async getCommunity(form: GetCommunity): Promise { - return this.wrapper(HttpType.Get, "/community", form); + async getCommunity(form: GetCommunity) { + return this.wrapper(HttpType.Get, "/community", form, GetCommunityResponse); } /** * Edit a community. + * * `HTTP.PUT /community` */ - async editCommunity(form: EditCommunity): Promise { - return this.wrapper(HttpType.Put, "/community", form); + async editCommunity(form: EditCommunity) { + return this.wrapper(HttpType.Put, "/community", form, CommunityResponse); } /** * List communities, with various filters. + * * `HTTP.GET /community/list` */ - async listCommunities( - form: ListCommunities - ): Promise { - return this.wrapper(HttpType.Get, "/community/list", form); + async listCommunities(form: ListCommunities) { + return this.wrapper( + HttpType.Get, + "/community/list", + form, + ListCommunitiesResponse + ); } /** * Follow / subscribe to a community. + * * `HTTP.POST /community/follow` */ - async followCommunity(form: FollowCommunity): Promise { - return this.wrapper(HttpType.Post, "/community/follow", form); + async followCommunity(form: FollowCommunity) { + return this.wrapper( + HttpType.Post, + "/community/follow", + form, + CommunityResponse + ); } /** * Block a community. + * * `HTTP.POST /community/block` */ - async blockCommunity(form: BlockCommunity): Promise { - return this.wrapper(HttpType.Post, "/community/block", form); + async blockCommunity(form: BlockCommunity) { + return this.wrapper( + HttpType.Post, + "/community/block", + form, + BlockCommunityResponse + ); } /** * Delete a community. + * * `HTTP.POST /community/delete` */ - async deleteCommunity(form: DeleteCommunity): Promise { - return this.wrapper(HttpType.Post, "/community/delete", form); + async deleteCommunity(form: DeleteCommunity) { + return this.wrapper( + HttpType.Post, + "/community/delete", + form, + CommunityResponse + ); } /** * A moderator remove for a community. + * * `HTTP.POST /community/remove` */ - async removeCommunity(form: RemoveCommunity): Promise { - return this.wrapper(HttpType.Post, "/community/remove", form); + async removeCommunity(form: RemoveCommunity) { + return this.wrapper( + HttpType.Post, + "/community/remove", + form, + CommunityResponse + ); } /** * Transfer your community to an existing moderator. + * * `HTTP.POST /community/transfer` */ - async transferCommunity( - form: TransferCommunity - ): Promise { - return this.wrapper(HttpType.Post, "/community/transfer", form); + async transferCommunity(form: TransferCommunity) { + return this.wrapper( + HttpType.Post, + "/community/transfer", + form, + GetCommunityResponse + ); } /** * Ban a user from a community. + * * `HTTP.POST /community/ban_user` */ - async banFromCommunity( - form: BanFromCommunity - ): Promise { - return this.wrapper(HttpType.Post, "/community/ban_user", form); + async banFromCommunity(form: BanFromCommunity) { + return this.wrapper( + HttpType.Post, + "/community/ban_user", + form, + BanFromCommunityResponse + ); } /** * Add a moderator to your community. + * * `HTTP.POST /community/mod` */ - async addModToCommunity( - form: AddModToCommunity - ): Promise { - return this.wrapper(HttpType.Post, "/community/mod", form); + async addModToCommunity(form: AddModToCommunity) { + return this.wrapper( + HttpType.Post, + "/community/mod", + form, + AddModToCommunityResponse + ); } /** * Create a post. + * * `HTTP.POST /post` */ - async createPost(form: CreatePost): Promise { - return this.wrapper(HttpType.Post, "/post", form); + async createPost(form: CreatePost) { + return this.wrapper(HttpType.Post, "/post", form, PostResponse); } /** * Get / fetch a post. + * * `HTTP.GET /post` */ - async getPost(form: GetPost): Promise { - return this.wrapper(HttpType.Get, "/post", form); + async getPost(form: GetPost) { + return this.wrapper(HttpType.Get, "/post", form, GetPostResponse); } /** * Edit a post. + * * `HTTP.PUT /post` */ - async editPost(form: EditPost): Promise { - return this.wrapper(HttpType.Put, "/post", form); + async editPost(form: EditPost) { + return this.wrapper(HttpType.Put, "/post", form, PostResponse); } /** * Delete a post. + * * `HTTP.POST /post/delete` */ - async deletePost(form: DeletePost): Promise { - return this.wrapper(HttpType.Post, "/post/delete", form); + async deletePost(form: DeletePost) { + return this.wrapper(HttpType.Post, "/post/delete", form, PostResponse); } /** * A moderator remove for a post. + * * `HTTP.POST /post/remove` */ - async removePost(form: RemovePost): Promise { - return this.wrapper(HttpType.Post, "/post/remove", form); + async removePost(form: RemovePost) { + return this.wrapper(HttpType.Post, "/post/remove", form, PostResponse); } /** * Mark a post as read. + * * `HTTP.POST /post/mark_as_read` */ - async markPostAsRead(form: MarkPostAsRead): Promise { - return this.wrapper(HttpType.Post, "/post/mark_as_read", form); + async markPostAsRead(form: MarkPostAsRead) { + return this.wrapper( + HttpType.Post, + "/post/mark_as_read", + form, + PostResponse + ); } /** * A moderator can lock a post ( IE disable new comments ). + * * `HTTP.POST /post/lock` */ - async lockPost(form: LockPost): Promise { - return this.wrapper(HttpType.Post, "/post/lock", form); + async lockPost(form: LockPost) { + return this.wrapper(HttpType.Post, "/post/lock", form, PostResponse); } /** * A moderator can sticky a post ( IE stick it to the top of a community ). + * * `HTTP.POST /post/sticky` */ - async stickyPost(form: StickyPost): Promise { - return this.wrapper(HttpType.Post, "/post/sticky", form); + async stickyPost(form: StickyPost) { + return this.wrapper(HttpType.Post, "/post/sticky", form, PostResponse); } /** * Get / fetch posts, with various filters. + * * `HTTP.GET /post/list` */ - async getPosts(form: GetPosts): Promise { - return this.wrapper(HttpType.Get, "/post/list", form); + async getPosts(form: GetPosts) { + return this.wrapper(HttpType.Get, "/post/list", form, GetPostsResponse); } /** * Like / vote on a post. + * * `HTTP.POST /post/like` */ - async likePost(form: CreatePostLike): Promise { - return this.wrapper(HttpType.Post, "/post/like", form); + async likePost(form: CreatePostLike) { + return this.wrapper(HttpType.Post, "/post/like", form, PostResponse); } /** * Save a post. + * * `HTTP.PUT /post/save` */ - async savePost(form: SavePost): Promise { - return this.wrapper(HttpType.Put, "/post/save", form); + async savePost(form: SavePost) { + return this.wrapper(HttpType.Put, "/post/save", form, PostResponse); } /** * Report a post. + * * `HTTP.POST /post/report` */ - async createPostReport(form: CreatePostReport): Promise { - return this.wrapper(HttpType.Post, "/post/report", form); + async createPostReport(form: CreatePostReport) { + return this.wrapper( + HttpType.Post, + "/post/report", + form, + PostReportResponse + ); } /** * Resolve a post report. Only a mod can do this. + * * `HTTP.PUT /post/report/resolve` */ - async resolvePostReport( - form: ResolvePostReport - ): Promise { - return this.wrapper(HttpType.Put, "/post/report/resolve", form); + async resolvePostReport(form: ResolvePostReport) { + return this.wrapper( + HttpType.Put, + "/post/report/resolve", + form, + PostReportResponse + ); } /** * List post reports. + * * `HTTP.GET /post/report/list` */ - async listPostReports( - form: ListPostReports - ): Promise { - return this.wrapper(HttpType.Get, "/post/report/list", form); + async listPostReports(form: ListPostReports) { + return this.wrapper( + HttpType.Get, + "/post/report/list", + form, + ListPostReportsResponse + ); } /** * Fetch metadata for any given site. + * * `HTTP.GET /post/site_metadata` */ - async getSiteMetadata( - form: GetSiteMetadata - ): Promise { - return this.wrapper(HttpType.Get, "/post/site_metadata", form); + async getSiteMetadata(form: GetSiteMetadata) { + return this.wrapper( + HttpType.Get, + "/post/site_metadata", + form, + GetSiteMetadataResponse + ); } /** * Create a comment. + * * `HTTP.POST /comment` */ - async createComment(form: CreateComment): Promise { - return this.wrapper(HttpType.Post, "/comment", form); + async createComment(form: CreateComment) { + return this.wrapper(HttpType.Post, "/comment", form, CommentResponse); } /** * Edit a comment. + * * `HTTP.PUT /comment` */ - async editComment(form: EditComment): Promise { - return this.wrapper(HttpType.Put, "/comment", form); + async editComment(form: EditComment) { + return this.wrapper(HttpType.Put, "/comment", form, CommentResponse); } /** * Delete a comment. + * * `HTTP.POST /comment/delete` */ - async deleteComment(form: DeleteComment): Promise { - return this.wrapper(HttpType.Post, "/comment/delete", form); + async deleteComment(form: DeleteComment) { + return this.wrapper( + HttpType.Post, + "/comment/delete", + form, + CommentResponse + ); } /** * A moderator remove for a comment. + * * `HTTP.POST /comment/remove` */ - async removeComment(form: RemoveComment): Promise { - return this.wrapper(HttpType.Post, "/comment/remove", form); + async removeComment(form: RemoveComment) { + return this.wrapper( + HttpType.Post, + "/comment/remove", + form, + CommentResponse + ); } /** * Mark a comment as read. + * * `HTTP.POST /comment/mark_as_read` */ - async markCommentAsRead(form: MarkCommentAsRead): Promise { - return this.wrapper(HttpType.Post, "/comment/mark_as_read", form); + async markCommentAsRead(form: MarkCommentAsRead) { + return this.wrapper( + HttpType.Post, + "/comment/mark_as_read", + form, + CommentResponse + ); } /** * Like / vote on a comment. + * * `HTTP.POST /comment/like` */ - async likeComment(form: CreateCommentLike): Promise { - return this.wrapper(HttpType.Post, "/comment/like", form); + async likeComment(form: CreateCommentLike) { + return this.wrapper(HttpType.Post, "/comment/like", form, CommentResponse); } /** * Save a comment. + * * `HTTP.PUT /comment/save` */ - async saveComment(form: SaveComment): Promise { - return this.wrapper(HttpType.Put, "/comment/save", form); + async saveComment(form: SaveComment) { + return this.wrapper(HttpType.Put, "/comment/save", form, CommentResponse); } /** * Get / fetch comments. + * * `HTTP.GET /comment/list` */ - async getComments(form: GetComments): Promise { - return this.wrapper(HttpType.Get, "/comment/list", form); + async getComments(form: GetComments) { + return this.wrapper( + HttpType.Get, + "/comment/list", + form, + GetCommentsResponse + ); } /** * Report a comment. + * * `HTTP.POST /comment/report` */ - async createCommentReport( - form: CreateCommentReport - ): Promise { - return this.wrapper(HttpType.Post, "/comment/report", form); + async createCommentReport(form: CreateCommentReport) { + return this.wrapper( + HttpType.Post, + "/comment/report", + form, + CommentReportResponse + ); } /** * Resolve a comment report. Only a mod can do this. + * * `HTTP.PUT /comment/report/resolve` */ - async resolveCommentReport( - form: ResolveCommentReport - ): Promise { - return this.wrapper(HttpType.Put, "/comment/report/resolve", form); + async resolveCommentReport(form: ResolveCommentReport) { + return this.wrapper( + HttpType.Put, + "/comment/report/resolve", + form, + CommentReportResponse + ); } /** * List comment reports. + * * `HTTP.GET /comment/report/list` */ - async listCommentReports( - form: ListCommentReports - ): Promise { - return this.wrapper(HttpType.Get, "/comment/report/list", form); + async listCommentReports(form: ListCommentReports) { + return this.wrapper( + HttpType.Get, + "/comment/report/list", + form, + ListCommentReportsResponse + ); } /** * Get / fetch private messages. + * * `HTTP.GET /private_message/list` */ - async getPrivateMessages( - form: GetPrivateMessages - ): Promise { - return this.wrapper(HttpType.Get, "/private_message/list", form); + async getPrivateMessages(form: GetPrivateMessages) { + return this.wrapper( + HttpType.Get, + "/private_message/list", + form, + PrivateMessagesResponse + ); } /** * Create a private message. + * * `HTTP.POST /private_message` */ - async createPrivateMessage( - form: CreatePrivateMessage - ): Promise { - return this.wrapper(HttpType.Post, "/private_message", form); + async createPrivateMessage(form: CreatePrivateMessage) { + return this.wrapper( + HttpType.Post, + "/private_message", + form, + PrivateMessageResponse + ); } /** * Edit a private message. + * * `HTTP.PUT /private_message` */ - async editPrivateMessage( - form: EditPrivateMessage - ): Promise { - return this.wrapper(HttpType.Put, "/private_message", form); + async editPrivateMessage(form: EditPrivateMessage) { + return this.wrapper( + HttpType.Put, + "/private_message", + form, + PrivateMessageResponse + ); } /** * Delete a private message. + * * `HTTP.POST /private_message/delete` */ - async deletePrivateMessage( - form: DeletePrivateMessage - ): Promise { - return this.wrapper(HttpType.Post, "/private_message/delete", form); + async deletePrivateMessage(form: DeletePrivateMessage) { + return this.wrapper( + HttpType.Post, + "/private_message/delete", + form, + PrivateMessageResponse + ); } /** * Mark a private message as read. + * * `HTTP.POST /private_message/mark_as_read` */ - async markPrivateMessageAsRead( - form: MarkPrivateMessageAsRead - ): Promise { - return this.wrapper(HttpType.Post, "/private_message/mark_as_read", form); + async markPrivateMessageAsRead(form: MarkPrivateMessageAsRead) { + return this.wrapper( + HttpType.Post, + "/private_message/mark_as_read", + form, + PrivateMessageResponse + ); } /** * Register a new user. + * * `HTTP.POST /user/register` */ - async register(form: Register): Promise { - return this.wrapper(HttpType.Post, "/user/register", form); + async register(form: Register) { + return this.wrapper(HttpType.Post, "/user/register", form, LoginResponse); } /** * Log into lemmy. + * * `HTTP.POST /user/login` */ - async login(form: Login): Promise { - return this.wrapper(HttpType.Post, "/user/login", form); + async login(form: Login) { + return this.wrapper(HttpType.Post, "/user/login", form, LoginResponse); } /** * Get the details for a person. + * * `HTTP.GET /user` */ - async getPersonDetails( - form: GetPersonDetails - ): Promise { - return this.wrapper(HttpType.Get, "/user", form); + async getPersonDetails(form: GetPersonDetails) { + return this.wrapper(HttpType.Get, "/user", form, GetPersonDetailsResponse); } /** * Get mentions for your user. + * * `HTTP.GET /user/mention` */ - async getPersonMentions( - form: GetPersonMentions - ): Promise { - return this.wrapper(HttpType.Get, "/user/mention", form); + async getPersonMentions(form: GetPersonMentions) { + return this.wrapper( + HttpType.Get, + "/user/mention", + form, + GetPersonMentionsResponse + ); } /** * Mark a person mention as read. + * * `HTTP.POST /user/mention/mark_as_read` */ - async markPersonMentionAsRead( - form: MarkPersonMentionAsRead - ): Promise { - return this.wrapper(HttpType.Post, "/user/mention/mark_as_read", form); + async markPersonMentionAsRead(form: MarkPersonMentionAsRead) { + return this.wrapper( + HttpType.Post, + "/user/mention/mark_as_read", + form, + PersonMentionResponse + ); } /** * Get comment replies. + * * `HTTP.GET /user/replies` */ - async getReplies(form: GetReplies): Promise { - return this.wrapper(HttpType.Get, "/user/replies", form); + async getReplies(form: GetReplies) { + return this.wrapper( + HttpType.Get, + "/user/replies", + form, + GetRepliesResponse + ); } /** * Ban a person from your site. + * * `HTTP.POST /user/ban` */ - async banPerson(form: BanPerson): Promise { - return this.wrapper(HttpType.Post, "/user/ban", form); + async banPerson(form: BanPerson) { + return this.wrapper(HttpType.Post, "/user/ban", form, BanPersonResponse); } /** * Get a list of banned users + * * `HTTP.GET /user/banned` */ - async getBannedPersons( - form: GetBannedPersons - ): Promise { - return this.wrapper(HttpType.Get, "/user/banned", form); + async getBannedPersons(form: GetBannedPersons) { + return this.wrapper( + HttpType.Get, + "/user/banned", + form, + BannedPersonsResponse + ); } /** * Block a person. + * * `HTTP.POST /user/block` */ - async blockPerson(form: BlockPerson): Promise { - return this.wrapper(HttpType.Post, "/user/block", form); + async blockPerson(form: BlockPerson) { + return this.wrapper( + HttpType.Post, + "/user/block", + form, + BlockPersonResponse + ); } /** * Fetch a Captcha. + * * `HTTP.GET /user/get_captcha` */ - async getCaptcha(): Promise { - return this.wrapper(HttpType.Get, "/user/get_captcha", {}); + async getCaptcha() { + return this.wrapper( + HttpType.Get, + "/user/get_captcha", + {}, + GetCaptchaResponse + ); } /** * Delete your account. + * * `HTTP.POST /user/delete_account` */ - async deleteAccount(form: DeleteAccount): Promise { - return this.wrapper(HttpType.Post, "/user/delete_account", form); + async deleteAccount(form: DeleteAccount) { + return this.wrapper( + HttpType.Post, + "/user/delete_account", + form, + DeleteAccountResponse + ); } /** * Reset your password. + * * `HTTP.POST /user/password_reset` */ - async passwordReset(form: PasswordReset): Promise { - return this.wrapper(HttpType.Post, "/user/password_reset", form); + async passwordReset(form: PasswordReset) { + return this.wrapper( + HttpType.Post, + "/user/password_reset", + form, + PasswordResetResponse + ); } /** * Change your password from an email / token based reset. + * * `HTTP.POST /user/password_change` */ - async passwordChange(form: PasswordChange): Promise { - return this.wrapper(HttpType.Post, "/user/password_change", form); + async passwordChange(form: PasswordChange) { + return this.wrapper( + HttpType.Post, + "/user/password_change", + form, + LoginResponse + ); } /** * Mark all replies as read. + * * `HTTP.POST /user/mark_all_as_read` */ - async markAllAsRead(form: MarkAllAsRead): Promise { - return this.wrapper(HttpType.Post, "/user/mark_all_as_read", form); + async markAllAsRead(form: MarkAllAsRead) { + return this.wrapper( + HttpType.Post, + "/user/mark_all_as_read", + form, + GetRepliesResponse + ); } /** * Save your user settings. + * * `HTTP.PUT /user/save_user_settings` */ - async saveUserSettings(form: SaveUserSettings): Promise { - return this.wrapper(HttpType.Put, "/user/save_user_settings", form); + async saveUserSettings(form: SaveUserSettings) { + return this.wrapper( + HttpType.Put, + "/user/save_user_settings", + form, + LoginResponse + ); } /** * Change your user password. + * * `HTTP.PUT /user/change_password` */ - async changePassword(form: ChangePassword): Promise { - return this.wrapper(HttpType.Put, "/user/change_password", form); + async changePassword(form: ChangePassword) { + return this.wrapper( + HttpType.Put, + "/user/change_password", + form, + LoginResponse + ); } /** * Get counts for your reports + * * `HTTP.GET /user/report_count` */ - async getReportCount(form: GetReportCount): Promise { - return this.wrapper(HttpType.Get, "/user/report_count", form); + async getReportCount(form: GetReportCount) { + return this.wrapper( + HttpType.Get, + "/user/report_count", + form, + GetReportCountResponse + ); } /** * Get your unread counts + * * `HTTP.GET /user/unread_count` */ - async getUnreadCount(form: GetUnreadCount): Promise { - return this.wrapper(HttpType.Get, "/user/unread_count", form); + async getUnreadCount(form: GetUnreadCount) { + return this.wrapper( + HttpType.Get, + "/user/unread_count", + form, + GetUnreadCountResponse + ); } /** * Verify your email + * * `HTTP.POST /user/verify_email` */ - async verifyEmail(form: VerifyEmail): Promise { - return this.wrapper(HttpType.Post, "/user/verify_email", form); + async verifyEmail(form: VerifyEmail) { + return this.wrapper( + HttpType.Post, + "/user/verify_email", + form, + VerifyEmailResponse + ); } /** * Add an admin to your site. + * * `HTTP.POST /admin/add` */ - async addAdmin(form: AddAdmin): Promise { - return this.wrapper(HttpType.Post, "/admin/add", form); + async addAdmin(form: AddAdmin) { + return this.wrapper(HttpType.Post, "/admin/add", form, AddAdminResponse); } /** * Get the unread registration applications count. + * * `HTTP.GET /admin/registration_application/count` */ async getUnreadRegistrationApplicationCount( form: GetUnreadRegistrationApplicationCount - ): Promise { + ) { return this.wrapper( HttpType.Get, "/admin/registration_application/count", - form + form, + GetUnreadRegistrationApplicationCountResponse ); } /** * List the registration applications. + * * `HTTP.GET /admin/registration_application/list` */ - async listRegistrationApplications( - form: ListRegistrationApplications - ): Promise { + async listRegistrationApplications(form: ListRegistrationApplications) { return this.wrapper( HttpType.Get, "/admin/registration_application/list", - form + form, + ListRegistrationApplicationsResponse ); } /** * Approve a registration application + * * `HTTP.PUT /admin/registration_application/approve` */ - async approveRegistrationApplication( - form: ApproveRegistrationApplication - ): Promise { + async approveRegistrationApplication(form: ApproveRegistrationApplication) { return this.wrapper( HttpType.Put, "/admin/registration_application/approve", - form + form, + RegistrationApplicationResponse ); } @@ -804,17 +1060,29 @@ export class LemmyHttp { return `${this.apiUrl}${endpoint}`; } - private async wrapper( + private async wrapper( type_: HttpType, endpoint: string, - form: MessageType + form: BodyType, + responseClass: ClassConstructor ): Promise { if (type_ == HttpType.Get) { let getUrl = `${this.buildFullUrl(endpoint)}?${encodeGetParams(form)}`; - return fetch(getUrl, { - method: "GET", - headers: this.headers, - }).then(d => d.json() as Promise); + return ( + fetch(getUrl, { + method: "GET", + headers: this.headers, + }) + // TODO test this + .then( + d => + d + .text() + .then(a => + deserialize(responseClass, a) + ) as Promise + ) + ); } else { return fetch(this.buildFullUrl(endpoint), { method: type_, @@ -822,14 +1090,19 @@ export class LemmyHttp { "Content-Type": "application/json", ...this.headers, }, - body: JSON.stringify(form), + body: serialize(form), }).then(d => d.json() as Promise); } } } -function encodeGetParams(p: any): string { - return Object.entries(p) - .map(kv => kv.map(encodeURIComponent).join("=")) - .join("&"); +function encodeGetParams(p: BodyType): string { + // Necessary to remove the Options + let serialized = JSON.parse(serialize(p)); + return ( + Object.entries(serialized) + // TODO test this, it might serialize the undefineds + .map(kv => kv.map(encodeURIComponent).join("=")) + .join("&") + ); } diff --git a/src/index.ts b/src/index.ts index dc09883..7477d8a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ export * from "./http"; export * from "./interfaces"; +export * from "./utils"; export * from "./websocket"; diff --git a/src/interfaces/api/comment.ts b/src/interfaces/api/comment.ts index 3ad4d0a..b4e2ee1 100644 --- a/src/interfaces/api/comment.ts +++ b/src/interfaces/api/comment.ts @@ -1,74 +1,122 @@ +import { Option } from "@sniptt/monads"; +import { Expose, Transform, Type } from "class-transformer"; +import "reflect-metadata"; +import { toOption, toUndefined } from "../../utils"; import { ListingType, SortType } from "../others"; import { CommentReportView, CommentView } from "../views"; -export interface CreateComment { +export class CreateComment { content: string; - parent_id?: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + parent_id: Option; post_id: number; /** * An optional front end ID, to tell which is comment is coming back. */ - form_id?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + form_id: Option; auth: string; + + constructor(init: CreateComment) { + Object.assign(this, init); + } } -export interface EditComment { +export class EditComment { content: string; comment_id: number; /** * An optional front end ID, to tell which is comment is coming back. */ - form_id?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + form_id: Option; auth: string; + + constructor(init: EditComment) { + Object.assign(this, init); + } } /** * Only the creator can delete the comment. */ -export interface DeleteComment { +export class DeleteComment { comment_id: number; deleted: boolean; auth: string; + + constructor(init: DeleteComment) { + Object.assign(this, init); + } } /** * Only a mod or admin can remove the comment. */ -export interface RemoveComment { +export class RemoveComment { comment_id: number; removed: boolean; - reason?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + reason: Option; auth: string; + + constructor(init: RemoveComment) { + Object.assign(this, init); + } } /** * Only the recipient can do this. */ -export interface MarkCommentAsRead { +export class MarkCommentAsRead { comment_id: number; read: boolean; auth: string; + + constructor(init: MarkCommentAsRead) { + Object.assign(this, init); + } } -export interface SaveComment { +export class SaveComment { comment_id: number; save: boolean; auth: string; + + constructor(init: SaveComment) { + Object.assign(this, init); + } } -export interface CommentResponse { +export class CommentResponse { + @Type(() => CommentView) comment_view: CommentView; recipient_ids: number[]; /** * An optional front end ID, to tell which is comment is coming back. */ - form_id?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + form_id: Option; } -export interface CreateCommentLike { +export class CreateCommentLike { comment_id: number; score: number; auth: string; + + constructor(init: CreateCommentLike) { + Object.assign(this, init); + } } /** @@ -77,55 +125,110 @@ export interface CreateCommentLike { * You can use either `community_id` or `community_name` as an id. * To get posts for a federated community by name, use `name@instance.tld` . */ -export interface GetComments { - type_?: ListingType; - sort?: SortType; - page?: number; - limit?: number; - community_id?: number; - community_name?: string; - saved_only?: boolean; - auth?: string; +export class GetComments { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + type_: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + sort: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + page: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + limit: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + community_id: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + community_name: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + saved_only: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + auth: Option; + + constructor(init: GetComments) { + Object.assign(this, init); + } } -export interface GetCommentsResponse { +export class GetCommentsResponse { + @Type(() => CommentView) comments: CommentView[]; } -export interface CreateCommentReport { +export class CreateCommentReport { comment_id: number; reason: string; auth: string; + + constructor(init: CreateCommentReport) { + Object.assign(this, init); + } } -export interface CommentReportResponse { +export class CommentReportResponse { + @Type(() => CommentReportView) comment_report_view: CommentReportView; } -export interface ResolveCommentReport { +export class ResolveCommentReport { report_id: number; /** * Either resolve or unresolve a report. */ resolved: boolean; auth: string; + + constructor(init: ResolveCommentReport) { + Object.assign(this, init); + } } -export interface ListCommentReports { - page?: number; - limit?: number; +export class ListCommentReports { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + page: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + limit: Option; /** * if no community is given, it returns reports for all communities moderated by the auth user. */ - community_id?: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + community_id: Option; /** * Only shows the unresolved reports. */ - unresolved_only?: boolean; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + unresolved_only: Option; auth: string; + + constructor(init: ListCommentReports) { + Object.assign(this, init); + } } -export interface ListCommentReportsResponse { +export class ListCommentReportsResponse { + @Type(() => CommentReportView) comment_reports: CommentReportView[]; } diff --git a/src/interfaces/api/community.ts b/src/interfaces/api/community.ts index a9c0ff3..b4f7099 100644 --- a/src/interfaces/api/community.ts +++ b/src/interfaces/api/community.ts @@ -1,3 +1,7 @@ +import { Option } from "@sniptt/monads"; +import { Expose, Transform, Type } from "class-transformer"; +import "reflect-metadata"; +import { toOption, toUndefined } from "../../utils"; import { ListingType, SortType } from "../others"; import { Site } from "../source"; import { @@ -11,47 +15,106 @@ import { * * To get a federated community by name, use `name@instance.tld` . */ -export interface GetCommunity { - id?: number; - name?: string; - auth?: string; +export class GetCommunity { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + id: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + name: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + auth: Option; + + constructor(init: GetCommunity) { + Object.assign(this, init); + } } -export interface GetCommunityResponse { +export class GetCommunityResponse { + @Type(() => CommunityView) community_view: CommunityView; - site?: Site; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + @Type(() => Site) + site: Option; + @Type(() => CommunityModeratorView) moderators: CommunityModeratorView[]; online: number; } -export interface CreateCommunity { +export class CreateCommunity { name: string; title: string; - description?: string; - icon?: string; - banner?: string; - nsfw?: boolean; - posting_restricted_to_mods?: boolean; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + description: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + icon: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + banner: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + nsfw: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + posting_restricted_to_mods: Option; auth: string; + + constructor(init: CreateCommunity) { + Object.assign(this, init); + } } -export interface CommunityResponse { +export class CommunityResponse { + @Type(() => CommunityView) community_view: CommunityView; } -export interface ListCommunities { - type_?: ListingType; - sort?: SortType; - page?: number; - limit?: number; - auth?: string; +export class ListCommunities { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + type_: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + sort: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + page: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + limit: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + auth: Option; + + constructor(init: ListCommunities) { + Object.assign(this, init); + } } -export interface ListCommunitiesResponse { +export class ListCommunitiesResponse { + @Type(() => CommunityView) communities: CommunityView[]; } -export interface BanFromCommunity { +export class BanFromCommunity { community_id: number; person_id: number; ban: boolean; @@ -59,84 +122,152 @@ export interface BanFromCommunity { /** * Removes/Restores their comments and posts for that community. */ - remove_data?: boolean; - reason?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + remove_data: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + reason: Option; /** * The expire time in Unix seconds */ - expires?: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + expires: Option; auth: string; + + constructor(init: BanFromCommunity) { + Object.assign(this, init); + } } -export interface BanFromCommunityResponse { +export class BanFromCommunityResponse { + @Type(() => PersonViewSafe) person_view: PersonViewSafe; banned: boolean; } -export interface AddModToCommunity { +export class AddModToCommunity { community_id: number; person_id: number; added: boolean; auth: string; + + constructor(init: AddModToCommunity) { + Object.assign(this, init); + } } -export interface AddModToCommunityResponse { +export class AddModToCommunityResponse { + @Type(() => CommunityModeratorView) moderators: CommunityModeratorView[]; } /** * Only mods can edit a community. */ -export interface EditCommunity { +export class EditCommunity { community_id: number; - title?: string; - description?: string; - icon?: string; - banner?: string; - nsfw?: boolean; - posting_restricted_to_mods?: boolean; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + title: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + description: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + icon: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + banner: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + nsfw: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + posting_restricted_to_mods: Option; auth: string; + + constructor(init: EditCommunity) { + Object.assign(this, init); + } } -export interface DeleteCommunity { +export class DeleteCommunity { community_id: number; deleted: boolean; auth: string; + + constructor(init: DeleteCommunity) { + Object.assign(this, init); + } } /** * Only admins can remove a community. */ -export interface RemoveCommunity { +export class RemoveCommunity { community_id: number; removed: boolean; - reason?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + reason: Option; /** * The expire time in Unix seconds */ - expires?: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + expires: Option; auth: string; + + constructor(init: RemoveCommunity) { + Object.assign(this, init); + } } -export interface FollowCommunity { +export class FollowCommunity { community_id: number; follow: boolean; auth: string; + + constructor(init: FollowCommunity) { + Object.assign(this, init); + } } -export interface TransferCommunity { +export class TransferCommunity { community_id: number; person_id: number; auth: string; + + constructor(init: TransferCommunity) { + Object.assign(this, init); + } } -export interface BlockCommunity { +export class BlockCommunity { community_id: number; block: boolean; auth: string; + + constructor(init: BlockCommunity) { + Object.assign(this, init); + } } -export interface BlockCommunityResponse { +export class BlockCommunityResponse { + @Type(() => CommunityView) community_view: CommunityView; blocked: boolean; } diff --git a/src/interfaces/api/person.ts b/src/interfaces/api/person.ts index ffc4e55..8eab211 100644 --- a/src/interfaces/api/person.ts +++ b/src/interfaces/api/person.ts @@ -1,3 +1,7 @@ +import { Option } from "@sniptt/monads"; +import { Expose, Transform, Type } from "class-transformer"; +import "reflect-metadata"; +import { toOption, toUndefined } from "../../utils"; import { SortType } from "../others"; import { CommentView, @@ -8,9 +12,13 @@ import { PrivateMessageView, } from "../views"; -export interface Login { +export class Login { username_or_email: string; password: string; + + constructor(init: Login) { + Object.assign(this, init); + } } /** @@ -18,37 +26,60 @@ export interface Login { * * Only the first user to register will be able to be the admin. */ -export interface Register { +export class Register { username: string; /** * Email is mandatory if email verification is enabled on the server */ - email?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + email: Option; password: string; password_verify: string; show_nsfw: boolean; /** * Captcha is only checked if these are enabled in the server. */ - captcha_uuid?: string; - captcha_answer?: string; - honeypot?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + captcha_uuid: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + captcha_answer: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + honeypot: Option; /** * An answer is mandatory if require application is enabled on the server */ - answer?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + answer: Option; + + constructor(init: Register) { + Object.assign(this, init); + } } -export interface GetCaptcha {} +export class GetCaptcha {} -export interface GetCaptchaResponse { +export class GetCaptchaResponse { /** * Will be undefined if captchas are disabled. */ - ok?: CaptchaResponse; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + @Type(() => CaptchaResponse) + ok: Option; } -export interface CaptchaResponse { +export class CaptchaResponse { /** * A Base64 encoded png. */ @@ -57,7 +88,10 @@ export interface CaptchaResponse { /** * A Base64 encoded wav file. */ - wav?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + wav: Option; /** * A UUID to match the one given on request. @@ -65,259 +99,489 @@ export interface CaptchaResponse { uuid: string; } -export interface SaveUserSettings { - show_nsfw?: boolean; +export class SaveUserSettings { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + show_nsfw: Option; /** * Default for this is `browser`. */ - theme?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + theme: Option; /** * The [[SortType]]. * * The Sort types from above, zero indexed as a number */ - default_sort_type?: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + default_sort_type: Option; /** * The [[ListingType]]. * * Post listing types are `All, Subscribed, Community` */ - default_listing_type?: number; - lang?: string; - avatar?: string; - banner?: string; - display_name?: string; - email?: string; - bio?: string; - matrix_user_id?: string; - show_avatars?: boolean; - show_scores?: boolean; - send_notifications_to_email?: boolean; - bot_account?: boolean; - show_bot_accounts?: boolean; - show_read_posts?: boolean; - show_new_post_notifs?: boolean; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + default_listing_type: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + lang: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + avatar: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + banner: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + display_name: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + email: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + bio: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + matrix_user_id: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + show_avatars: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + show_scores: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + send_notifications_to_email: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + bot_account: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + show_bot_accounts: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + show_read_posts: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + show_new_post_notifs: Option; auth: string; + + constructor(init: SaveUserSettings) { + Object.assign(this, init); + } } -export interface ChangePassword { +export class ChangePassword { new_password: string; new_password_verify: string; old_password: string; auth: string; + + constructor(init: ChangePassword) { + Object.assign(this, init); + } } /** * The `jwt` string should be stored and used anywhere `auth` is called for. */ -export interface LoginResponse { +export class LoginResponse { /** * This is None in response to `Register` if email verification is enabled, or the server requires registration applications. */ - jwt?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + jwt: Option; verify_email_sent: boolean; registration_created: boolean; } -export interface GetPersonDetails { - person_id?: number; +export class GetPersonDetails { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + person_id: Option; /** * To get details for a federated user, use `person@instance.tld`. */ - username?: string; - sort?: SortType; - page?: number; - limit?: number; - community_id?: number; - saved_only?: boolean; - auth?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + username: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + sort: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + page: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + limit: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + community_id: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + saved_only: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + auth: Option; + + constructor(init: GetPersonDetails) { + Object.assign(this, init); + } } -export interface GetPersonDetailsResponse { +export class GetPersonDetailsResponse { + @Type(() => PersonViewSafe) person_view: PersonViewSafe; + @Type(() => CommentView) comments: CommentView[]; + @Type(() => PostView) posts: PostView[]; + @Type(() => CommunityModeratorView) moderates: CommunityModeratorView[]; } -export interface GetRepliesResponse { +export class GetRepliesResponse { + @Type(() => CommentView) replies: CommentView[]; } -export interface GetPersonMentionsResponse { +export class GetPersonMentionsResponse { + @Type(() => PersonMentionView) mentions: PersonMentionView[]; } -export interface MarkAllAsRead { +export class MarkAllAsRead { auth: string; + + constructor(auth: string) { + this.auth = auth; + } } -export interface AddAdmin { +export class AddAdmin { person_id: number; added: boolean; auth: string; + + constructor(init: AddAdmin) { + Object.assign(this, init); + } } -export interface AddAdminResponse { +export class AddAdminResponse { + @Type(() => PersonViewSafe) admins: PersonViewSafe[]; } -export interface BanPerson { +export class BanPerson { person_id: number; ban: boolean; /** * Removes/Restores their comments, posts, and communities */ - remove_data?: boolean; - reason?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + remove_data: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + reason: Option; /** * The expire time in Unix seconds */ - expires?: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + expires: Option; auth: string; + + constructor(init: BanPerson) { + Object.assign(this, init); + } } -export interface BanPersonResponse { +export class BanPersonResponse { + @Type(() => PersonViewSafe) person_view: PersonViewSafe; banned: boolean; } -export interface GetReplies { - sort?: SortType; - page?: number; - limit?: number; - unread_only?: boolean; +export class GetReplies { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + sort: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + page: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + limit: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + unread_only: Option; auth: string; + + constructor(init: GetReplies) { + Object.assign(this, init); + } } -export interface GetPersonMentions { - sort?: SortType; - page?: number; - limit?: number; - unread_only?: boolean; +export class GetPersonMentions { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + sort: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + page: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + limit: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + unread_only: Option; auth: string; + + constructor(init: GetPersonMentions) { + Object.assign(this, init); + } } -export interface MarkPersonMentionAsRead { +export class MarkPersonMentionAsRead { person_mention_id: number; read: boolean; auth: string; + + constructor(init: MarkPersonMentionAsRead) { + Object.assign(this, init); + } } -export interface PersonMentionResponse { +export class PersonMentionResponse { + @Type(() => PersonMentionView) person_mention_view: PersonMentionView; } /** * Permanently deletes your posts and comments */ -export interface DeleteAccount { +export class DeleteAccount { password: string; auth: string; + + constructor(init: DeleteAccount) { + Object.assign(this, init); + } } -export interface DeleteAccountResponse {} +export class DeleteAccountResponse {} -export interface PasswordReset { +export class PasswordReset { email: string; + + constructor(init: PasswordReset) { + Object.assign(this, init); + } } -export interface PasswordResetResponse {} +export class PasswordResetResponse {} -export interface PasswordChange { +export class PasswordChange { token: string; password: string; password_verify: string; + + constructor(init: PasswordChange) { + Object.assign(this, init); + } } -export interface CreatePrivateMessage { +export class CreatePrivateMessage { content: string; recipient_id: number; auth: string; + + constructor(init: CreatePrivateMessage) { + Object.assign(this, init); + } } -export interface EditPrivateMessage { +export class EditPrivateMessage { private_message_id: number; content: string; auth: string; + + constructor(init: EditPrivateMessage) { + Object.assign(this, init); + } } -export interface DeletePrivateMessage { +export class DeletePrivateMessage { private_message_id: number; deleted: boolean; auth: string; + + constructor(init: DeletePrivateMessage) { + Object.assign(this, init); + } } -export interface MarkPrivateMessageAsRead { +export class MarkPrivateMessageAsRead { private_message_id: number; read: boolean; auth: string; + + constructor(init: MarkPrivateMessageAsRead) { + Object.assign(this, init); + } } -export interface GetPrivateMessages { - unread_only?: boolean; - page?: number; - limit?: number; +export class GetPrivateMessages { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + unread_only: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + page: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + limit: Option; auth: string; + + constructor(init: GetPrivateMessages) { + Object.assign(this, init); + } } -export interface PrivateMessagesResponse { +export class PrivateMessagesResponse { + @Type(() => PrivateMessageView) private_messages: PrivateMessageView[]; } -export interface PrivateMessageResponse { +export class PrivateMessageResponse { + @Type(() => PrivateMessageView) private_message_view: PrivateMessageView; } -export interface GetReportCount { +export class GetReportCount { /** * If a community is supplied, returns the report count for only that community, otherwise returns the report count for all communities the user moderates. */ - community_id?: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + community_id: Option; auth: string; + + constructor(init: GetReportCount) { + Object.assign(this, init); + } } -export interface GetReportCountResponse { - community_id?: number; +export class GetReportCountResponse { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + community_id: Option; comment_reports: number; post_reports: number; } -export interface GetUnreadCount { +export class GetUnreadCount { auth: string; + + constructor(init: GetUnreadCount) { + Object.assign(this, init); + } } -export interface GetUnreadCountResponse { +export class GetUnreadCountResponse { replies: number; mentions: number; private_messages: number; } -export interface VerifyEmail { +export class VerifyEmail { token: string; + + constructor(init: VerifyEmail) { + Object.assign(this, init); + } } -export interface VerifyEmailResponse {} +export class VerifyEmailResponse {} -export interface BlockPerson { +export class BlockPerson { person_id: number; block: boolean; auth: string; + + constructor(init: BlockPerson) { + Object.assign(this, init); + } } -export interface BlockPersonResponse { +export class BlockPersonResponse { + @Type(() => PersonViewSafe) person_view: PersonViewSafe; blocked: boolean; } -export interface GetBannedPersons { +export class GetBannedPersons { auth: string; + + constructor(init: GetBannedPersons) { + Object.assign(this, init); + } } -export interface BannedPersonsResponse { +export class BannedPersonsResponse { + @Type(() => PersonViewSafe) banned: PersonViewSafe[]; } diff --git a/src/interfaces/api/post.ts b/src/interfaces/api/post.ts index 7afb0e8..8cf116d 100644 --- a/src/interfaces/api/post.ts +++ b/src/interfaces/api/post.ts @@ -1,3 +1,7 @@ +import { Option } from "@sniptt/monads"; +import { Expose, Transform, Type } from "class-transformer"; +import "reflect-metadata"; +import { toOption, toUndefined } from "../../utils"; import { ListingType, SiteMetadata, SortType } from "../others"; import { CommentView, @@ -7,52 +11,109 @@ import { PostView, } from "../views"; -export interface CreatePost { +export class CreatePost { name: string; - url?: string; - body?: string; - nsfw?: boolean; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + url: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + body: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + nsfw: Option; community_id: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + honeypot: Option; auth: string; - honeypot?: string; + + constructor(init: CreatePost) { + Object.assign(this, init); + } } -export interface PostResponse { +export class PostResponse { + @Type(() => PostView) post_view: PostView; } -export interface GetPost { +export class GetPost { id: number; - auth?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + auth: Option; + + constructor(init: GetPost) { + Object.assign(this, init); + } } -export interface GetPostResponse { +export class GetPostResponse { + @Type(() => PostView) post_view: PostView; + @Type(() => CommunityView) community_view: CommunityView; + @Type(() => CommentView) comments: CommentView[]; + @Type(() => CommunityModeratorView) moderators: CommunityModeratorView[]; online: number; } -export interface GetPosts { - type_?: ListingType; - sort?: SortType; - page?: number; - limit?: number; - community_id?: number; +export class GetPosts { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + type_: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + sort: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + page: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + limit: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + community_id: Option; /** * To get posts for a federated community by name, use `name@instance.tld` . */ - community_name?: string; - saved_only?: boolean; - auth?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + community_name: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + saved_only: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + auth: Option; + + constructor(init: GetPosts) { + Object.assign(this, init); + } } -export interface GetPostsResponse { +export class GetPostsResponse { + @Type(() => PostView) posts: PostView[]; } -export interface CreatePostLike { +export class CreatePostLike { post_id: number; /** @@ -60,107 +121,185 @@ export interface CreatePostLike { */ score: number; auth: string; + + constructor(init: CreatePostLike) { + Object.assign(this, init); + } } -export interface EditPost { +export class EditPost { post_id: number; - name?: string; - url?: string; - body?: string; - nsfw?: boolean; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + name: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + url: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + body: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + nsfw: Option; auth: string; + + constructor(init: EditPost) { + Object.assign(this, init); + } } -export interface DeletePost { +export class DeletePost { post_id: number; deleted: boolean; auth: string; + + constructor(init: DeletePost) { + Object.assign(this, init); + } } /** * Only admins and mods can remove a post. */ -export interface RemovePost { +export class RemovePost { post_id: number; removed: boolean; - reason?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + reason: Option; auth: string; + + constructor(init: RemovePost) { + Object.assign(this, init); + } } /** * Marks a post as read. */ -export interface MarkPostAsRead { +export class MarkPostAsRead { post_id: number; read: boolean; auth: string; + + constructor(init: MarkPostAsRead) { + Object.assign(this, init); + } } /** * Only admins and mods can lock a post. */ -export interface LockPost { +export class LockPost { post_id: number; locked: boolean; auth: string; + + constructor(init: LockPost) { + Object.assign(this, init); + } } /** * Only admins and mods can sticky a post. */ -export interface StickyPost { +export class StickyPost { post_id: number; stickied: boolean; auth: string; + + constructor(init: StickyPost) { + Object.assign(this, init); + } } -export interface SavePost { +export class SavePost { post_id: number; save: boolean; auth: string; + + constructor(init: SavePost) { + Object.assign(this, init); + } } -export interface CreatePostReport { +export class CreatePostReport { post_id: number; reason: string; auth: string; + + constructor(init: CreatePostReport) { + Object.assign(this, init); + } } -export interface PostReportResponse { +export class PostReportResponse { + @Type(() => PostReportView) post_report_view: PostReportView; } -export interface ResolvePostReport { +export class ResolvePostReport { report_id: number; /** * Either resolve or unresolve a report. */ resolved: boolean; auth: string; + + constructor(init: ResolvePostReport) { + Object.assign(this, init); + } } -export interface ListPostReports { - page?: number; - limit?: number; +export class ListPostReports { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + page: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + limit: Option; /** * if no community is given, it returns reports for all communities moderated by the auth user. */ - community_id?: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + community_id: Option; /** * Only shows the unresolved reports. */ - unresolved_only?: boolean; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + unresolved_only: Option; auth: string; + + constructor(init: ListPostReports) { + Object.assign(this, init); + } } -export interface ListPostReportsResponse { +export class ListPostReportsResponse { + @Type(() => PostReportView) post_reports: PostReportView[]; } -export interface GetSiteMetadata { +export class GetSiteMetadata { url: string; + + constructor(init: GetSiteMetadata) { + Object.assign(this, init); + } } -export interface GetSiteMetadataResponse { +export class GetSiteMetadataResponse { + @Type(() => SiteMetadata) metadata: SiteMetadata; } diff --git a/src/interfaces/api/site.ts b/src/interfaces/api/site.ts index 2726277..fe2eae1 100644 --- a/src/interfaces/api/site.ts +++ b/src/interfaces/api/site.ts @@ -1,3 +1,7 @@ +import { Option } from "@sniptt/monads"; +import { Expose, Transform, Type } from "class-transformer"; +import "reflect-metadata"; +import { toOption, toUndefined } from "../../utils"; import { ListingType, SearchType, SortType } from "../others"; import { CommentView, @@ -26,191 +30,450 @@ import { /** * Search lemmy for different types of data. */ -export interface Search { +export class Search { /** * The search query string. */ q: string; - type_?: SearchType; - community_id?: number; - community_name?: string; - creator_id?: number; - sort?: SortType; - listing_type?: ListingType; - page?: number; - limit?: number; - auth?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + type_: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + community_id: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + community_name: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + creator_id: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + sort: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + listing_type: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + page: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + limit: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + auth: Option; + + constructor(init: Search) { + Object.assign(this, init); + } } -export interface SearchResponse { +export class SearchResponse { /** * The [[SearchType]]. */ type_: string; + @Type(() => CommentView) comments: CommentView[]; + @Type(() => PostView) posts: PostView[]; + @Type(() => CommunityView) communities: CommunityView[]; + @Type(() => PersonViewSafe) users: PersonViewSafe[]; } -export interface GetModlog { - mod_person_id?: number; - community_id?: number; - page?: number; - limit?: number; - auth?: string; +export class GetModlog { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + mod_person_id: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + community_id: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + page: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + limit: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + auth: Option; + + constructor(init: GetModlog) { + Object.assign(this, init); + } } -export interface GetModlogResponse { +export class GetModlogResponse { + @Type(() => ModRemovePostView) removed_posts: ModRemovePostView[]; + @Type(() => ModLockPostView) locked_posts: ModLockPostView[]; + @Type(() => ModStickyPostView) stickied_posts: ModStickyPostView[]; + @Type(() => ModRemoveCommentView) removed_comments: ModRemoveCommentView[]; + @Type(() => ModRemoveCommunityView) removed_communities: ModRemoveCommunityView[]; + @Type(() => ModBanFromCommunityView) banned_from_community: ModBanFromCommunityView[]; + @Type(() => ModBanView) banned: ModBanView[]; + @Type(() => ModAddCommunityView) added_to_community: ModAddCommunityView[]; + @Type(() => ModTransferCommunityView) transferred_to_community: ModTransferCommunityView[]; + @Type(() => ModAddView) added: ModAddView[]; } -export interface CreateSite { +export class CreateSite { name: string; - sidebar?: string; - description?: string; - icon?: string; - banner?: string; - enable_downvotes?: boolean; - open_registration?: boolean; - enable_nsfw?: boolean; - community_creation_admin_only?: boolean; - require_email_verification?: boolean; - require_application?: boolean; - application_question?: string; - private_instance?: boolean; - default_theme?: string; - default_post_listing_type?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + sidebar: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + description: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + icon: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + banner: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + enable_downvotes: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + open_registration: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + enable_nsfw: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + community_creation_admin_only: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + require_email_verification: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + require_application: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + application_question: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + private_instance: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + default_theme: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + default_post_listing_type: Option; auth: string; + + constructor(init: CreateSite) { + Object.assign(this, init); + } } -export interface EditSite { - name?: string; - sidebar?: string; - description?: string; - icon?: string; - banner?: string; - enable_downvotes?: boolean; - open_registration?: boolean; - enable_nsfw?: boolean; - community_creation_admin_only?: boolean; - require_email_verification?: boolean; - require_application?: boolean; - application_question?: string; - private_instance?: boolean; - default_theme?: string; - legal_information?: string; - default_post_listing_type?: string; +export class EditSite { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + name: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + sidebar: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + description: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + icon: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + banner: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + enable_downvotes: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + open_registration: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + enable_nsfw: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + community_creation_admin_only: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + require_email_verification: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + require_application: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + application_question: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + private_instance: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + default_theme: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + legal_information: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + default_post_listing_type: Option; auth: string; + + constructor(init: EditSite) { + Object.assign(this, init); + } } -export interface GetSite { - auth?: string; +export class GetSite { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + auth: Option; + + constructor(init: GetSite) { + Object.assign(this, init); + } } -export interface SiteResponse { +export class SiteResponse { + @Type(() => SiteView) site_view: SiteView; } -export interface GetSiteResponse { +export class GetSiteResponse { /** * Optional, because the site might not be set up yet. */ - site_view?: SiteView; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + @Type(() => SiteView) + site_view: Option; + @Type(() => PersonViewSafe) admins: PersonViewSafe[]; online: number; version: string; /** * If you're logged in, you'll get back extra user info. */ - my_user?: MyUserInfo; - federated_instances?: FederatedInstances; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + @Type(() => MyUserInfo) + my_user: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + @Type(() => FederatedInstances) + federated_instances: Option; } /** * Your user info, such as blocks, follows, etc. */ -export interface MyUserInfo { +export class MyUserInfo { + @Type(() => LocalUserSettingsView) local_user_view: LocalUserSettingsView; + @Type(() => CommunityFollowerView) follows: CommunityFollowerView[]; + @Type(() => CommunityModeratorView) moderates: CommunityModeratorView[]; + @Type(() => CommunityBlockView) community_blocks: CommunityBlockView[]; + @Type(() => PersonBlockView) person_blocks: PersonBlockView[]; } -export interface LeaveAdmin { +export class LeaveAdmin { auth: string; + + constructor(init: LeaveAdmin) { + Object.assign(this, init); + } } -export interface GetSiteConfig { +export class GetSiteConfig { auth: string; + + constructor(init: GetSiteConfig) { + Object.assign(this, init); + } } -export interface GetSiteConfigResponse { +export class GetSiteConfigResponse { config_hjson: string; } -export interface SaveSiteConfig { +export class SaveSiteConfig { config_hjson: string; auth: string; + + constructor(init: SaveSiteConfig) { + Object.assign(this, init); + } } -export interface FederatedInstances { +export class FederatedInstances { linked: string[]; - allowed?: string[]; - blocked?: string[]; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + allowed: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + blocked: Option; + + constructor(init: FederatedInstances) { + Object.assign(this, init); + } } -export interface ResolveObject { +export class ResolveObject { q: string; - auth?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + auth: Option; + + constructor(init: ResolveObject) { + Object.assign(this, init); + } } -export interface ResolveObjectResponse { - comment?: CommentView; - post?: PostView; - community?: CommunityView; - person?: PersonViewSafe; +export class ResolveObjectResponse { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + @Type(() => CommentView) + comment: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + @Type(() => PostView) + post: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + @Type(() => CommunityView) + community: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + @Type(() => PersonViewSafe) + person: Option; } -export interface ListRegistrationApplications { +export class ListRegistrationApplications { /** * Only shows the unread applications (IE those without an admin actor) */ - unread_only?: boolean; - page?: number; - limit?: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + unread_only: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + page: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + limit: Option; auth: string; + + constructor(init: ListRegistrationApplications) { + Object.assign(this, init); + } } -export interface ListRegistrationApplicationsResponse { +export class ListRegistrationApplicationsResponse { + @Type(() => RegistrationApplicationView) registration_applications: RegistrationApplicationView[]; } -export interface ApproveRegistrationApplication { +export class ApproveRegistrationApplication { id: number; approve: boolean; - deny_reason?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + deny_reason: Option; auth: string; + + constructor(init: ApproveRegistrationApplication) { + Object.assign(this, init); + } } -export interface RegistrationApplicationResponse { +export class RegistrationApplicationResponse { + @Type(() => RegistrationApplicationView) registration_application: RegistrationApplicationView; } -export interface GetUnreadRegistrationApplicationCount { +export class GetUnreadRegistrationApplicationCount { auth: string; + + constructor(init: GetUnreadRegistrationApplicationCount) { + Object.assign(this, init); + } } -export interface GetUnreadRegistrationApplicationCountResponse { +export class GetUnreadRegistrationApplicationCountResponse { registration_applications: number; } diff --git a/src/interfaces/others.ts b/src/interfaces/others.ts index 7676d7f..2b257fc 100644 --- a/src/interfaces/others.ts +++ b/src/interfaces/others.ts @@ -1,3 +1,6 @@ +import { Option } from "@sniptt/monads"; +import { Expose, Transform } from "class-transformer"; +import { toOption, toUndefined } from "../utils"; export const VERSION = "v3"; /** @@ -148,48 +151,28 @@ export enum SearchType { Url = "Url", } -/** - * A websocket response. Includes the return type. - * Can be used like: - * - * ```ts - * if (op == UserOperation.Search) { - * let data = wsJsonToRes(msg).data; - * } - * ``` - */ -export interface WebSocketResponse { - op: UserOperation; - /** - * This contains the data for a websocket response. - * - * The correct response type if given is in [[LemmyHttp]]. - */ - data: ResponseType; -} - -/** - * A websocket JSON response that includes the errors. - */ -export interface WebSocketJsonResponse { - op?: string; - - /** - * This contains the data for a websocket response. - * - * The correct response type if given is in [[LemmyHttp]]. - */ - data?: ResponseType; - error?: string; - reconnect?: boolean; -} - /** * A holder for a site's metadata ( such as opengraph tags ), used for post links. */ -export interface SiteMetadata { - title?: string; - description?: string; - image?: string; - html?: string; +export class SiteMetadata { + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + title: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + description: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + image: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + html: Option; + + constructor(init: SiteMetadata) { + Object.assign(this, init); + } } diff --git a/src/interfaces/source.ts b/src/interfaces/source.ts index 199d8e4..cf542cb 100644 --- a/src/interfaces/source.ts +++ b/src/interfaces/source.ts @@ -1,7 +1,14 @@ -export interface LocalUserSettings { +import { Option } from "@sniptt/monads"; +import { Expose, Transform } from "class-transformer"; +import { toOption, toUndefined } from "../utils"; + +export class LocalUserSettings { id: number; person_id: number; - email?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + email: Option; show_nsfw: boolean; theme: string; default_sort_type: number; @@ -17,43 +24,82 @@ export interface LocalUserSettings { accepted_application: boolean; } -export interface PersonSafe { +export class PersonSafe { id: number; name: string; - display_name?: string; - avatar?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + display_name: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + avatar: Option; banned: boolean; published: string; - updated?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + updated: Option; actor_id: string; - bio?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + bio: Option; local: boolean; - banner?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + banner: Option; deleted: boolean; inbox_url: string; shared_inbox_url: string; - matrix_user_id?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + matrix_user_id: Option; admin: boolean; bot_account: boolean; - ban_expires?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + ban_expires: Option; } -export interface Site { +export class Site { id: number; name: string; - sidebar?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + sidebar: Option; published: string; - updated?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + updated: Option; enable_downvotes: boolean; open_registration: boolean; enable_nsfw: boolean; - icon?: string; - banner?: string; - description?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + icon: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + banner: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + description: Option; community_creation_admin_only: boolean; require_email_verification: boolean; require_application: boolean; - application_question?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + application_question: Option; private_instance: boolean; default_theme: string; default_post_listing_type: string; @@ -61,10 +107,13 @@ export interface Site { last_refreshed_at: string; inbox_url: string; public_key: string; - legal_information?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + legal_information: Option; } -export interface PrivateMessage { +export class PrivateMessage { id: number; creator_id: number; recipient_id: number; @@ -72,190 +121,304 @@ export interface PrivateMessage { deleted: boolean; read: boolean; published: string; - updated?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + updated: Option; ap_id: string; local: boolean; } -export interface PostReport { +export class PostReport { id: number; creator_id: number; post_id: number; original_post_name: string; - original_post_url?: string; - original_post_body?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + original_post_url: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + original_post_body: Option; reason: string; resolved: boolean; - resolver_id?: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + resolver_id: Option; published: string; - updated?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + updated: Option; } -export interface Post { +export class Post { id: number; name: string; - url?: string; - body?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + url: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + body: Option; creator_id: number; community_id: number; removed: boolean; locked: boolean; published: string; - updated?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + updated: Option; deleted: boolean; nsfw: boolean; stickied: boolean; - embed_title?: string; - embed_description?: string; - embed_html?: string; - thumbnail_url?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + embed_title: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + embed_description: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + embed_html: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + thumbnail_url: Option; ap_id: string; local: boolean; } -export interface PasswordResetRequest { +export class PasswordResetRequest { id: number; local_user_id: number; token_encrypted: string; published: string; } -export interface ModRemovePost { +export class ModRemovePost { id: number; mod_person_id: number; post_id: number; - reason?: string; - removed?: boolean; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + reason: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + removed: Option; when_: string; } -export interface ModLockPost { +export class ModLockPost { id: number; mod_person_id: number; post_id: number; - locked?: boolean; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + locked: Option; when_: string; } -export interface ModStickyPost { +export class ModStickyPost { id: number; mod_person_id: number; post_id: number; - stickied?: boolean; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + stickied: Option; when_: string; } -export interface ModRemoveComment { +export class ModRemoveComment { id: number; mod_person_id: number; comment_id: number; - reason?: string; - removed?: boolean; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + reason: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + removed: Option; when_: string; } -export interface ModRemoveCommunity { +export class ModRemoveCommunity { id: number; mod_person_id: number; community_id: number; - reason?: string; - removed?: boolean; - expires?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + reason: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + removed: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + expires: Option; when_: string; } -export interface ModBanFromCommunity { +export class ModBanFromCommunity { id: number; mod_person_id: number; other_person_id: number; community_id: number; - reason?: string; - banned?: boolean; - expires?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + reason: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + banned: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + expires: Option; when_: string; } -export interface ModBan { +export class ModBan { id: number; mod_person_id: number; other_person_id: number; - reason?: string; - banned?: boolean; - expires?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + reason: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + banned: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + expires: Option; when_: string; } -export interface ModAddCommunity { +export class ModAddCommunity { id: number; mod_person_id: number; other_person_id: number; community_id: number; - removed?: boolean; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + removed: Option; when_: string; } -export interface ModTransferCommunity { +export class ModTransferCommunity { id: number; mod_person_id: number; other_person_id: number; community_id: number; - removed?: boolean; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + removed: Option; when_: string; } -export interface ModAdd { +export class ModAdd { id: number; mod_person_id: number; other_person_id: number; - removed?: boolean; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + removed: Option; when_: string; } -export interface CommunitySafe { +export class CommunitySafe { id: number; name: string; title: string; - description?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + description: Option; removed: boolean; published: string; - updated?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + updated: Option; deleted: boolean; nsfw: boolean; actor_id: string; local: boolean; - icon?: string; - banner?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + icon: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + banner: Option; posting_restricted_to_mods: boolean; } -export interface CommentReport { +export class CommentReport { id: number; creator_id: number; comment_id: number; original_comment_text: string; reason: string; resolved: boolean; - resolver_id?: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + resolver_id: Option; published: string; - updated?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + updated: Option; } -export interface Comment { +export class Comment { id: number; creator_id: number; post_id: number; - parent_id?: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + parent_id: Option; content: string; removed: boolean; read: boolean; // Whether the recipient has read the comment or not published: string; - updated?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + updated: Option; deleted: boolean; ap_id: string; local: boolean; } -export interface PersonMention { +export class PersonMention { id: number; recipient_id: number; comment_id: number; @@ -263,11 +426,17 @@ export interface PersonMention { published: string; } -export interface RegistrationApplication { +export class RegistrationApplication { id: number; local_user_id: number; answer: string; - admin_id?: number; - deny_reason?: string; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + admin_id: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + deny_reason: Option; published: string; } diff --git a/src/interfaces/views.ts b/src/interfaces/views.ts index 574be90..93a5213 100644 --- a/src/interfaces/views.ts +++ b/src/interfaces/views.ts @@ -1,3 +1,7 @@ +import { Option } from "@sniptt/monads"; +import { Expose, Transform, Type } from "class-transformer"; +import "reflect-metadata"; +import { toOption, toUndefined } from "../utils"; import { CommentAggregates, CommunityAggregates, @@ -29,46 +33,65 @@ import { Site, } from "./source"; -export interface PersonViewSafe { +export class PersonViewSafe { + @Type(() => PersonSafe) person: PersonSafe; counts: PersonAggregates; } -export interface PersonMentionView { +export class PersonMentionView { + @Type(() => PersonMention) person_mention: PersonMention; + @Type(() => Comment) comment: Comment; + @Type(() => PersonSafe) creator: PersonSafe; + @Type(() => Post) post: Post; + @Type(() => CommunitySafe) community: CommunitySafe; + @Type(() => PersonSafe) recipient: PersonSafe; counts: CommentAggregates; creator_banned_from_community: boolean; subscribed: boolean; saved: boolean; creator_blocked: boolean; - my_vote?: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + my_vote: Option; } -export interface LocalUserSettingsView { +export class LocalUserSettingsView { + @Type(() => LocalUserSettings) local_user: LocalUserSettings; + @Type(() => PersonSafe) person: PersonSafe; counts: PersonAggregates; } -export interface SiteView { +export class SiteView { + @Type(() => Site) site: Site; counts: SiteAggregates; } -export interface PrivateMessageView { +export class PrivateMessageView { + @Type(() => PrivateMessage) private_message: PrivateMessage; + @Type(() => PersonSafe) creator: PersonSafe; + @Type(() => PersonSafe) recipient: PersonSafe; } -export interface PostView { +export class PostView { + @Type(() => Post) post: Post; + @Type(() => PersonSafe) creator: PersonSafe; + @Type(() => CommunitySafe) community: CommunitySafe; creator_banned_from_community: boolean; counts: PostAggregates; @@ -76,152 +99,248 @@ export interface PostView { saved: boolean; read: boolean; creator_blocked: boolean; - my_vote?: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + my_vote: Option; } -export interface PostReportView { +export class PostReportView { + @Type(() => PostReport) post_report: PostReport; + @Type(() => Post) post: Post; + @Type(() => CommunitySafe) community: CommunitySafe; + @Type(() => PersonSafe) creator: PersonSafe; + @Type(() => PersonSafe) post_creator: PersonSafe; creator_banned_from_community: boolean; - my_vote?: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + my_vote: Option; counts: PostAggregates; - resolver?: PersonSafe; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + @Type(() => PersonSafe) + resolver: Option; } -export interface CommentView { +export class CommentView { + @Type(() => Comment) comment: Comment; + @Type(() => PersonSafe) creator: PersonSafe; - recipient?: PersonSafe; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + @Type(() => PersonSafe) + recipient: Option; + @Type(() => Post) post: Post; + @Type(() => CommunitySafe) community: CommunitySafe; counts: CommentAggregates; creator_banned_from_community: boolean; subscribed: boolean; saved: boolean; creator_blocked: boolean; - my_vote?: number; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + my_vote: Option; } -export interface CommentReportView { +export class CommentReportView { + @Type(() => CommentReport) comment_report: CommentReport; + @Type(() => Comment) comment: Comment; + @Type(() => Post) post: Post; + @Type(() => CommunitySafe) community: CommunitySafe; + @Type(() => PersonSafe) creator: PersonSafe; + @Type(() => PersonSafe) comment_creator: PersonSafe; counts: CommentAggregates; creator_banned_from_community: boolean; - my_vote?: number; - resolver?: PersonSafe; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + my_vote: Option; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + @Type(() => PersonSafe) + resolver: Option; } -export interface ModAddCommunityView { +export class ModAddCommunityView { + @Type(() => ModAddCommunity) mod_add_community: ModAddCommunity; + @Type(() => PersonSafe) moderator: PersonSafe; + @Type(() => CommunitySafe) community: CommunitySafe; + @Type(() => PersonSafe) modded_person: PersonSafe; } -export interface ModTransferCommunityView { +export class ModTransferCommunityView { + @Type(() => ModTransferCommunity) mod_transfer_community: ModTransferCommunity; + @Type(() => PersonSafe) moderator: PersonSafe; + @Type(() => CommunitySafe) community: CommunitySafe; + @Type(() => PersonSafe) modded_person: PersonSafe; } -export interface ModAddView { +export class ModAddView { + @Type(() => ModAdd) mod_add: ModAdd; + @Type(() => PersonSafe) moderator: PersonSafe; + @Type(() => PersonSafe) modded_person: PersonSafe; } -export interface ModBanFromCommunityView { +export class ModBanFromCommunityView { + @Type(() => ModBanFromCommunity) mod_ban_from_community: ModBanFromCommunity; + @Type(() => PersonSafe) moderator: PersonSafe; + @Type(() => CommunitySafe) community: CommunitySafe; + @Type(() => PersonSafe) banned_person: PersonSafe; } -export interface ModBanView { +export class ModBanView { + @Type(() => ModBan) mod_ban: ModBan; + @Type(() => PersonSafe) moderator: PersonSafe; + @Type(() => PersonSafe) banned_person: PersonSafe; } -export interface ModLockPostView { +export class ModLockPostView { + @Type(() => ModLockPost) mod_lock_post: ModLockPost; + @Type(() => PersonSafe) moderator: PersonSafe; + @Type(() => Post) post: Post; + @Type(() => CommunitySafe) community: CommunitySafe; } -export interface ModRemoveCommentView { +export class ModRemoveCommentView { + @Type(() => ModRemoveComment) mod_remove_comment: ModRemoveComment; + @Type(() => PersonSafe) moderator: PersonSafe; + @Type(() => Comment) comment: Comment; + @Type(() => PersonSafe) commenter: PersonSafe; + @Type(() => Post) post: Post; + @Type(() => CommunitySafe) community: CommunitySafe; } -export interface ModRemoveCommunityView { +export class ModRemoveCommunityView { + @Type(() => ModRemoveCommunity) mod_remove_community: ModRemoveCommunity; + @Type(() => PersonSafe) moderator: PersonSafe; + @Type(() => CommunitySafe) community: CommunitySafe; } -export interface ModRemovePostView { +export class ModRemovePostView { + @Type(() => ModRemovePost) mod_remove_post: ModRemovePost; + @Type(() => PersonSafe) moderator: PersonSafe; + @Type(() => Post) post: Post; + @Type(() => CommunitySafe) community: CommunitySafe; } -export interface ModStickyPostView { +export class ModStickyPostView { + @Type(() => ModStickyPost) mod_sticky_post: ModStickyPost; + @Type(() => PersonSafe) moderator: PersonSafe; + @Type(() => Post) post: Post; + @Type(() => CommunitySafe) community: CommunitySafe; } -export interface CommunityFollowerView { +export class CommunityFollowerView { + @Type(() => CommunitySafe) community: CommunitySafe; + @Type(() => PersonSafe) follower: PersonSafe; } -export interface CommunityBlockView { +export class CommunityBlockView { + @Type(() => PersonSafe) person: PersonSafe; + @Type(() => CommunitySafe) community: CommunitySafe; } -export interface CommunityModeratorView { +export class CommunityModeratorView { + @Type(() => CommunitySafe) community: CommunitySafe; + @Type(() => PersonSafe) moderator: PersonSafe; } -export interface CommunityPersonBanView { +export class CommunityPersonBanView { + @Type(() => CommunitySafe) community: CommunitySafe; + @Type(() => PersonSafe) person: PersonSafe; } -export interface PersonBlockView { +export class PersonBlockView { + @Type(() => PersonSafe) person: PersonSafe; + @Type(() => PersonSafe) target: PersonSafe; } -export interface CommunityView { +export class CommunityView { + @Type(() => CommunitySafe) community: CommunitySafe; subscribed: boolean; blocked: boolean; counts: CommunityAggregates; } -export interface RegistrationApplicationView { +export class RegistrationApplicationView { + @Type(() => RegistrationApplication) registration_application: RegistrationApplication; + @Type(() => LocalUserSettings) creator_local_user: LocalUserSettings; + @Type(() => PersonSafe) creator: PersonSafe; - admin?: PersonSafe; + @Transform(({ value }) => toOption(value), { toClassOnly: true }) + @Transform(({ value }) => toUndefined(value), { toPlainOnly: true }) + @Expose() + @Type(() => PersonSafe) + admin: Option; } diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..2fb61f1 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,15 @@ +import { Option, Some } from "@sniptt/monads"; + +/** + * Converts an option to an undefined. Necessary for API requests. + */ +export function toUndefined(opt: Option) { + return opt.isSome() ? opt.unwrap() : undefined; +} + +/** + * Converts a null value to an option. + */ +export function toOption(val: T): Option { + return Some(val || undefined); +} diff --git a/src/websocket.ts b/src/websocket.ts index 6c2da7c..4c7822b 100644 --- a/src/websocket.ts +++ b/src/websocket.ts @@ -1,3 +1,4 @@ +import { ClassConstructor, deserialize, serialize } from "class-transformer"; import { CreateComment, CreateCommentLike, @@ -499,7 +500,7 @@ export class LemmyWebsocket { /** * Gets the site, and your user data. */ - getSite(form: GetSite = {}) { + getSite(form: GetSite) { return wrapper(UserOperation.GetSite, form); } @@ -645,7 +646,22 @@ export class LemmyWebsocket { } function wrapper(op: UserOperation, data: MessageType) { - let send = { op: UserOperation[op], data: data }; - console.log(send); - return JSON.stringify(send); + let send = serialize({ op: UserOperation[op], data }); + return send; +} + +export function wsUserOp(msg: any): UserOperation { + let opStr: string = msg.op; + return UserOperation[opStr as keyof typeof UserOperation]; +} + +/** + * Converts a websocket string response to a usable result + */ +export function wsJsonToRes( + msg: any, + responseClass: ClassConstructor +): ResponseType { + // Have to deserialize the response again into the correct class + return deserialize(responseClass, serialize(msg.data)); } diff --git a/tsconfig.json b/tsconfig.json index b3be051..77d0598 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,7 @@ "lib": ["es2017", "es7", "es6", "dom"], "outDir": "./dist", "target": "ES5", + "experimentalDecorators": true, "moduleResolution": "Node" }, "include": [ diff --git a/yarn.lock b/yarn.lock index 518f8b9..f948357 100644 --- a/yarn.lock +++ b/yarn.lock @@ -251,6 +251,11 @@ "@nodelib/fs.scandir" "2.1.4" fastq "^1.6.0" +"@sniptt/monads@^0.5.10": + version "0.5.10" + resolved "https://registry.yarnpkg.com/@sniptt/monads/-/monads-0.5.10.tgz#a80cd00738bbd682d36d36dd36bdc0bddc96eb76" + integrity sha512-+agDOv9DpDV+9e2zN/Vmdk+XaqGx5Sykl0fqhqgiJ90r18nsBkxe44DmZ2sA1HYK+MSsBeZBiAr6pq4w+5uhfw== + "@types/glob@^7.1.1": version "7.2.0" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" @@ -539,6 +544,11 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +class-transformer@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" + integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== + clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -1706,6 +1716,11 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +reflect-metadata@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" + integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== + regexpp@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"