From 931bdc6aace5e7aa71ffdfb470e208ead78a2a53 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 2 Nov 2018 03:32:24 +0900 Subject: Refactoring, Clean up and bug fixes --- src/server/api/endpoints/users/followers.ts | 65 ++++++++++------- src/server/api/endpoints/users/following.ts | 65 ++++++++++------- .../users/get_frequently_replied_users.ts | 33 ++++++--- src/server/api/endpoints/users/lists/delete.ts | 8 ++- src/server/api/endpoints/users/lists/push.ts | 33 +++++---- src/server/api/endpoints/users/lists/show.ts | 19 +++-- src/server/api/endpoints/users/lists/update.ts | 14 ++-- src/server/api/endpoints/users/notes.ts | 82 +++++++++++++--------- src/server/api/endpoints/users/relation.ts | 8 ++- src/server/api/endpoints/users/search.ts | 20 +++--- src/server/api/endpoints/users/show.ts | 71 ++++++++++++------- 11 files changed, 260 insertions(+), 158 deletions(-) (limited to 'src/server/api/endpoints/users') diff --git a/src/server/api/endpoints/users/followers.ts b/src/server/api/endpoints/users/followers.ts index 7fe3ca9943..71081835c7 100644 --- a/src/server/api/endpoints/users/followers.ts +++ b/src/server/api/endpoints/users/followers.ts @@ -1,32 +1,49 @@ -import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; +import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; import User, { ILocalUser } from '../../../../models/user'; import Following from '../../../../models/following'; import { pack } from '../../../../models/user'; import { getFriendIds } from '../../common/get-friends'; +import getParams from '../../get-params'; -/** - * Get followers of a user - */ -export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { - // Get 'userId' parameter - const [userId, userIdErr] = $.type(ID).get(params.userId); - if (userIdErr) return rej('invalid userId param'); +export const meta = { + desc: { + 'ja-JP': '指定したユーザーのフォロワー一覧を取得します。', + 'en-US': 'Get followers of a user.' + }, + + requireCredential: false, - // Get 'iknow' parameter - const [iknow = false, iknowErr] = $.bool.optional.get(params.iknow); - if (iknowErr) return rej('invalid iknow param'); + params: { + userId: { + validator: $.type(ID), + transform: transform, + }, - // Get 'limit' parameter - const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit); - if (limitErr) return rej('invalid limit param'); + limit: { + validator: $.num.optional.range(1, 100), + default: 10 + }, - // Get 'cursor' parameter - const [cursor = null, cursorErr] = $.type(ID).optional.get(params.cursor); - if (cursorErr) return rej('invalid cursor param'); + cursor: { + validator: $.type(ID).optional, + default: null as any, + transform: transform, + }, + + iknow: { + validator: $.bool.optional, + default: false, + } + } +}; + +export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { + const [ps, psErr] = getParams(meta, params); + if (psErr) return rej(psErr); // Lookup user const user = await User.findOne({ - _id: userId + _id: ps.userId }, { fields: { _id: true @@ -43,7 +60,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => } as any; // ログインしていてかつ iknow フラグがあるとき - if (me && iknow) { + if (me && ps.iknow) { // Get my friends const myFriends = await getFriendIds(me._id); @@ -53,29 +70,27 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => } // カーソルが指定されている場合 - if (cursor) { + if (ps.cursor) { query._id = { - $lt: cursor + $lt: ps.cursor }; } // Get followers const following = await Following .find(query, { - limit: limit + 1, + limit: ps.limit + 1, sort: { _id: -1 } }); // 「次のページ」があるかどうか - const inStock = following.length === limit + 1; + const inStock = following.length === ps.limit + 1; if (inStock) { following.pop(); } - // Serialize const users = await Promise.all(following.map(f => pack(f.followerId, me, { detail: true }))); - // Response res({ users: users, next: inStock ? following[following.length - 1]._id : null, diff --git a/src/server/api/endpoints/users/following.ts b/src/server/api/endpoints/users/following.ts index 0e564fd1b6..778ef54a27 100644 --- a/src/server/api/endpoints/users/following.ts +++ b/src/server/api/endpoints/users/following.ts @@ -1,32 +1,49 @@ -import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; +import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; import User, { ILocalUser } from '../../../../models/user'; import Following from '../../../../models/following'; import { pack } from '../../../../models/user'; import { getFriendIds } from '../../common/get-friends'; +import getParams from '../../get-params'; -/** - * Get following users of a user - */ -export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { - // Get 'userId' parameter - const [userId, userIdErr] = $.type(ID).get(params.userId); - if (userIdErr) return rej('invalid userId param'); +export const meta = { + desc: { + 'ja-JP': '指定したユーザーのフォロー一覧を取得します。', + 'en-US': 'Get following users of a user.' + }, + + requireCredential: false, - // Get 'iknow' parameter - const [iknow = false, iknowErr] = $.bool.optional.get(params.iknow); - if (iknowErr) return rej('invalid iknow param'); + params: { + userId: { + validator: $.type(ID), + transform: transform, + }, - // Get 'limit' parameter - const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit); - if (limitErr) return rej('invalid limit param'); + limit: { + validator: $.num.optional.range(1, 100), + default: 10 + }, - // Get 'cursor' parameter - const [cursor = null, cursorErr] = $.type(ID).optional.get(params.cursor); - if (cursorErr) return rej('invalid cursor param'); + cursor: { + validator: $.type(ID).optional, + default: null as any, + transform: transform, + }, + + iknow: { + validator: $.bool.optional, + default: false, + } + } +}; + +export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { + const [ps, psErr] = getParams(meta, params); + if (psErr) return rej(psErr); // Lookup user const user = await User.findOne({ - _id: userId + _id: ps.userId }, { fields: { _id: true @@ -43,7 +60,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => } as any; // ログインしていてかつ iknow フラグがあるとき - if (me && iknow) { + if (me && ps.iknow) { // Get my friends const myFriends = await getFriendIds(me._id); @@ -53,29 +70,27 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => } // カーソルが指定されている場合 - if (cursor) { + if (ps.cursor) { query._id = { - $lt: cursor + $lt: ps.cursor }; } // Get followers const following = await Following .find(query, { - limit: limit + 1, + limit: ps.limit + 1, sort: { _id: -1 } }); // 「次のページ」があるかどうか - const inStock = following.length === limit + 1; + const inStock = following.length === ps.limit + 1; if (inStock) { following.pop(); } - // Serialize const users = await Promise.all(following.map(f => pack(f.followeeId, me, { detail: true }))); - // Response res({ users: users, next: inStock ? following[following.length - 1]._id : null, diff --git a/src/server/api/endpoints/users/get_frequently_replied_users.ts b/src/server/api/endpoints/users/get_frequently_replied_users.ts index 42b6ce20d6..b0fd259588 100644 --- a/src/server/api/endpoints/users/get_frequently_replied_users.ts +++ b/src/server/api/endpoints/users/get_frequently_replied_users.ts @@ -1,19 +1,31 @@ -import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; +import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; import Note from '../../../../models/note'; import User, { pack, ILocalUser } from '../../../../models/user'; +import getParams from '../../get-params'; -export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { - // Get 'userId' parameter - const [userId, userIdErr] = $.type(ID).get(params.userId); - if (userIdErr) return rej('invalid userId param'); +export const meta = { + requireCredential: false, + + params: { + userId: { + validator: $.type(ID), + transform: transform, + }, - // Get 'limit' parameter - const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit); - if (limitErr) return rej('invalid limit param'); + limit: { + validator: $.num.optional.range(1, 100), + default: 10 + }, + } +}; + +export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { + const [ps, psErr] = getParams(meta, params); + if (psErr) return rej(psErr); // Lookup user const user = await User.findOne({ - _id: userId + _id: ps.userId }, { fields: { _id: true @@ -83,7 +95,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => const repliedUsersSorted = Object.keys(repliedUsers).sort((a, b) => repliedUsers[b] - repliedUsers[a]); // Extract top replied users - const topRepliedUsers = repliedUsersSorted.slice(0, limit); + const topRepliedUsers = repliedUsersSorted.slice(0, ps.limit); // Make replies object (includes weights) const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({ @@ -91,6 +103,5 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => weight: repliedUsers[user] / peak }))); - // Response res(repliesObj); }); diff --git a/src/server/api/endpoints/users/lists/delete.ts b/src/server/api/endpoints/users/lists/delete.ts index c56963aab6..1d4513a822 100644 --- a/src/server/api/endpoints/users/lists/delete.ts +++ b/src/server/api/endpoints/users/lists/delete.ts @@ -1,5 +1,5 @@ import $ from 'cafy'; -import ID from '../../../../../misc/cafy-id'; +import ID, { transform } from '../../../../../misc/cafy-id'; import UserList from '../../../../../models/user-list'; import { ILocalUser } from '../../../../../models/user'; import getParams from '../../../get-params'; @@ -15,12 +15,14 @@ export const meta = { kind: 'account-write', params: { - listId: $.type(ID).note({ + listId: { + validator: $.type(ID), + transform: transform, desc: { 'ja-JP': '対象となるユーザーリストのID', 'en-US': 'ID of target user list' } - }) + } } }; diff --git a/src/server/api/endpoints/users/lists/push.ts b/src/server/api/endpoints/users/lists/push.ts index 2d68ec7458..8208e627ed 100644 --- a/src/server/api/endpoints/users/lists/push.ts +++ b/src/server/api/endpoints/users/lists/push.ts @@ -1,10 +1,11 @@ -import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; +import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import UserList from '../../../../../models/user-list'; import User, { pack as packUser, isRemoteUser, getGhost, ILocalUser } from '../../../../../models/user'; import { publishUserListStream } from '../../../../../stream'; import ap from '../../../../../remote/activitypub/renderer'; import renderFollow from '../../../../../remote/activitypub/renderer/follow'; import { deliver } from '../../../../../queue'; +import getParams from '../../../get-params'; export const meta = { desc: { @@ -14,20 +15,28 @@ export const meta = { requireCredential: true, - kind: 'account-write' + kind: 'account-write', + + params: { + listId: { + validator: $.type(ID), + transform: transform, + }, + + userId: { + validator: $.type(ID), + transform: transform, + }, + } }; -/** - * Add a user to a user list - */ export default async (params: any, me: ILocalUser) => new Promise(async (res, rej) => { - // Get 'listId' parameter - const [listId, listIdErr] = $.type(ID).get(params.listId); - if (listIdErr) return rej('invalid listId param'); + const [ps, psErr] = getParams(meta, params); + if (psErr) return rej(psErr); // Fetch the list const userList = await UserList.findOne({ - _id: listId, + _id: ps.listId, userId: me._id, }); @@ -35,13 +44,9 @@ export default async (params: any, me: ILocalUser) => new Promise(async (res, re return rej('list not found'); } - // Get 'userId' parameter - const [userId, userIdErr] = $.type(ID).get(params.userId); - if (userIdErr) return rej('invalid userId param'); - // Fetch the user const user = await User.findOne({ - _id: userId + _id: ps.userId }); if (user == null) { diff --git a/src/server/api/endpoints/users/lists/show.ts b/src/server/api/endpoints/users/lists/show.ts index a2dd00c6e1..585833a2fe 100644 --- a/src/server/api/endpoints/users/lists/show.ts +++ b/src/server/api/endpoints/users/lists/show.ts @@ -1,6 +1,7 @@ -import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; +import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import UserList, { pack } from '../../../../../models/user-list'; import { ILocalUser } from '../../../../../models/user'; +import getParams from '../../../get-params'; export const meta = { desc: { @@ -10,17 +11,23 @@ export const meta = { requireCredential: true, - kind: 'account-read' + kind: 'account-read', + + params: { + listId: { + validator: $.type(ID), + transform: transform, + }, + } }; export default async (params: any, me: ILocalUser) => new Promise(async (res, rej) => { - // Get 'listId' parameter - const [listId, listIdErr] = $.type(ID).get(params.listId); - if (listIdErr) return rej('invalid listId param'); + const [ps, psErr] = getParams(meta, params); + if (psErr) return rej(psErr); // Fetch the list const userList = await UserList.findOne({ - _id: listId, + _id: ps.listId, userId: me._id, }); diff --git a/src/server/api/endpoints/users/lists/update.ts b/src/server/api/endpoints/users/lists/update.ts index e39f66fb5b..fb1a37b2f1 100644 --- a/src/server/api/endpoints/users/lists/update.ts +++ b/src/server/api/endpoints/users/lists/update.ts @@ -1,5 +1,5 @@ import $ from 'cafy'; -import ID from '../../../../../misc/cafy-id'; +import ID, { transform } from '../../../../../misc/cafy-id'; import UserList, { pack } from '../../../../../models/user-list'; import { ILocalUser } from '../../../../../models/user'; import getParams from '../../../get-params'; @@ -15,18 +15,22 @@ export const meta = { kind: 'account-write', params: { - listId: $.type(ID).note({ + listId: { + validator: $.type(ID), + transform: transform, desc: { 'ja-JP': '対象となるユーザーリストのID', 'en-US': 'ID of target user list' } - }), - title: $.str.range(1, 100).note({ + }, + + title: { + validator: $.str.range(1, 100), desc: { 'ja-JP': 'このユーザーリストの名前', 'en-US': 'name of this user list' } - }) + } } }; diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts index 6934f7c4be..ac6601f64f 100644 --- a/src/server/api/endpoints/users/notes.ts +++ b/src/server/api/endpoints/users/notes.ts @@ -1,4 +1,4 @@ -import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; +import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; import getHostLower from '../../common/get-host-lower'; import Note, { packMany } from '../../../../models/note'; import User, { ILocalUser } from '../../../../models/user'; @@ -11,100 +11,118 @@ export const meta = { }, params: { - userId: $.type(ID).optional.note({ + userId: { + validator: $.type(ID).optional, + transform: transform, desc: { 'ja-JP': 'ユーザーID' } - }), + }, - username: $.str.optional.note({ + username: { + validator: $.str.optional, desc: { 'ja-JP': 'ユーザー名' } - }), + }, - host: $.str.optional.note({ - }), + host: { + validator: $.str.optional, + }, - includeReplies: $.bool.optional.note({ + includeReplies: { + validator: $.bool.optional, default: true, desc: { 'ja-JP': 'リプライを含めるか否か' } - }), + }, - limit: $.num.optional.range(1, 100).note({ + limit: { + validator: $.num.optional.range(1, 100), default: 10, desc: { 'ja-JP': '最大数' } - }), + }, - sinceId: $.type(ID).optional.note({ + sinceId: { + validator: $.type(ID).optional, + transform: transform, desc: { 'ja-JP': '指定すると、この投稿を基点としてより新しい投稿を取得します' } - }), + }, - untilId: $.type(ID).optional.note({ + untilId: { + validator: $.type(ID).optional, + transform: transform, desc: { 'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します' } - }), + }, - sinceDate: $.num.optional.note({ + sinceDate: { + validator: $.num.optional, desc: { 'ja-JP': '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。' } - }), + }, - untilDate: $.num.optional.note({ + untilDate: { + validator: $.num.optional, desc: { 'ja-JP': '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。' } - }), + }, - includeMyRenotes: $.bool.optional.note({ + includeMyRenotes: { + validator: $.bool.optional, default: true, desc: { 'ja-JP': '自分の行ったRenoteを含めるかどうか' } - }), + }, - includeRenotedMyNotes: $.bool.optional.note({ + includeRenotedMyNotes: { + validator: $.bool.optional, default: true, desc: { 'ja-JP': 'Renoteされた自分の投稿を含めるかどうか' } - }), + }, - includeLocalRenotes: $.bool.optional.note({ + includeLocalRenotes: { + validator: $.bool.optional, default: true, desc: { 'ja-JP': 'Renoteされたローカルの投稿を含めるかどうか' } - }), + }, - withFiles: $.bool.optional.note({ + withFiles: { + validator: $.bool.optional, default: false, desc: { 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します' } - }), + }, - mediaOnly: $.bool.optional.note({ + mediaOnly: { + validator: $.bool.optional, default: false, desc: { 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)' } - }), + }, - fileType: $.arr($.str).optional.note({ + fileType: { + validator: $.arr($.str).optional, desc: { 'ja-JP': '指定された種類のファイルが添付された投稿のみを取得します' } - }), + }, } }; @@ -121,7 +139,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified'; } - const q = ps.userId !== undefined + const q = ps.userId != null ? { _id: ps.userId } : { usernameLower: ps.username.toLowerCase(), host: getHostLower(ps.host) } ; diff --git a/src/server/api/endpoints/users/relation.ts b/src/server/api/endpoints/users/relation.ts index 5c7da48349..19643ceed0 100644 --- a/src/server/api/endpoints/users/relation.ts +++ b/src/server/api/endpoints/users/relation.ts @@ -1,4 +1,4 @@ -import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; +import $ from 'cafy'; import ID, { transform, ObjectId } from '../../../../misc/cafy-id'; import { ILocalUser, getRelation } from '../../../../models/user'; import getParams from '../../get-params'; @@ -10,11 +10,13 @@ export const meta = { requireCredential: true, params: { - userId: $.or($.type(ID), $.arr($.type(ID)).unique()).note({ + userId: { + validator: $.or($.type(ID), $.arr($.type(ID)).unique()), + transform: (v: any): ObjectId | ObjectId[] => Array.isArray(v) ? v.map(x => transform(x)) : transform(v), desc: { 'ja-JP': 'ユーザーID (配列でも可)' } - }) + } } }; diff --git a/src/server/api/endpoints/users/search.ts b/src/server/api/endpoints/users/search.ts index 307a8f6894..a2077b589d 100644 --- a/src/server/api/endpoints/users/search.ts +++ b/src/server/api/endpoints/users/search.ts @@ -11,32 +11,36 @@ export const meta = { requireCredential: false, params: { - query: $.str.note({ + query: { + validator: $.str, desc: { 'ja-JP': 'クエリ' } - }), + }, - offset: $.num.optional.min(0).note({ + offset: { + validator: $.num.optional.min(0), default: 0, desc: { 'ja-JP': 'オフセット' } - }), + }, - limit: $.num.optional.range(1, 100).note({ + limit: { + validator: $.num.optional.range(1, 100), default: 10, desc: { 'ja-JP': '取得する数' } - }), + }, - localOnly: $.bool.optional.note({ + localOnly: { + validator: $.bool.optional, default: false, desc: { 'ja-JP': 'ローカルユーザーのみ検索対象にするか否か' } - }), + }, }, }; diff --git a/src/server/api/endpoints/users/show.ts b/src/server/api/endpoints/users/show.ts index dd09bd5b91..c6f01593d4 100644 --- a/src/server/api/endpoints/users/show.ts +++ b/src/server/api/endpoints/users/show.ts @@ -1,35 +1,54 @@ -import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; +import $ from 'cafy'; import ID, { transform, transformMany } from '../../../../misc/cafy-id'; import User, { pack, ILocalUser, isRemoteUser } from '../../../../models/user'; import resolveRemoteUser from '../../../../remote/resolve-user'; +import getParams from '../../get-params'; const cursorOption = { fields: { data: false } }; -/** - * Show user(s) - */ -export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { - let user; +export const meta = { + desc: { + 'ja-JP': '指定したユーザーの情報を取得します。' + }, + + requireCredential: false, - // Get 'userId' parameter - const [userId, userIdErr] = $.type(ID).optional.get(params.userId); - if (userIdErr) return rej('invalid userId param'); + params: { + userId: { + validator: $.type(ID).optional, + transform: transform, + desc: { + 'ja-JP': 'ユーザーID' + } + }, - // Get 'userIds' parameter - const [userIds, userIdsErr] = $.arr($.type(ID)).optional.get(params.userIds); - if (userIdsErr) return rej('invalid userIds param'); + userIds: { + validator: $.arr($.type(ID)).optional.unique(), + transform: transformMany, + desc: { + 'ja-JP': 'ユーザーID (配列)' + } + }, - // Get 'username' parameter - const [username, usernameErr] = $.str.optional.get(params.username); - if (usernameErr) return rej('invalid username param'); + username: { + validator: $.str.optional + }, - // Get 'host' parameter - const [host, hostErr] = $.str.optional.nullable.get(params.host); - if (hostErr) return rej('invalid host param'); + host: { + validator: $.str.optional.nullable + } + } +}; + +export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { + const [ps, psErr] = getParams(meta, params); + if (psErr) return rej(psErr); + + let user; - if (userIds) { + if (ps.userIds) { const users = await User.find({ _id: { - $in: userIds + $in: ps.userIds } }); @@ -38,17 +57,17 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => })))); } else { // Lookup user - if (typeof host === 'string') { + if (typeof ps.host === 'string') { try { - user = await resolveRemoteUser(username, host, cursorOption); + user = await resolveRemoteUser(ps.username, ps.host, cursorOption); } catch (e) { console.warn(`failed to resolve remote user: ${e}`); return rej('failed to resolve remote user'); } } else { - const q: any = userId !== undefined - ? { _id: userId } - : { usernameLower: username.toLowerCase(), host: null }; + const q: any = ps.userId != null + ? { _id: ps.userId } + : { usernameLower: ps.username.toLowerCase(), host: null }; user = await User.findOne(q, cursorOption); @@ -64,7 +83,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => if (isRemoteUser(user)) { if (user.updatedAt == null || Date.now() - user.updatedAt.getTime() > 1000 * 60 * 60 * 24) { - resolveRemoteUser(username, host, { }, true); + resolveRemoteUser(ps.username, ps.host, { }, true); } } } -- cgit v1.2.3-freya