diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2019-02-22 11:46:58 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-02-22 11:46:58 +0900 |
| commit | 2756f553c68082342a784ef716c62da6cea6f3ca (patch) | |
| tree | 1e0364ca9ddc1fd88e311f0687746f44e007effd /src/server/api/endpoints/notes | |
| parent | Update CHANGELOG.md (diff) | |
| download | sharkey-2756f553c68082342a784ef716c62da6cea6f3ca.tar.gz sharkey-2756f553c68082342a784ef716c62da6cea6f3ca.tar.bz2 sharkey-2756f553c68082342a784ef716c62da6cea6f3ca.zip | |
Improve error handling of API (#4345)
* wip
* wip
* wip
* Update attached_notes.ts
* wip
* Refactor
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Update call.ts
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* :v:
* Fix
Diffstat (limited to 'src/server/api/endpoints/notes')
25 files changed, 490 insertions, 355 deletions
diff --git a/src/server/api/endpoints/notes/conversation.ts b/src/server/api/endpoints/notes/conversation.ts index 848cf9e162..ec371ecf19 100644 --- a/src/server/api/endpoints/notes/conversation.ts +++ b/src/server/api/endpoints/notes/conversation.ts @@ -2,6 +2,8 @@ import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; import Note, { packMany, INote } from '../../../../models/note'; import define from '../../define'; +import { ApiError } from '../../error'; +import { getValiedNote } from '../../common/getters'; export const meta = { desc: { @@ -30,19 +32,23 @@ export const meta = { validator: $.optional.num.min(0), default: 0 }, + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: 'e1035875-9551-45ec-afa8-1ded1fcb53c8' + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // Lookup note - const note = await Note.findOne({ - _id: ps.noteId +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; }); - if (note === null) { - return rej('note not found'); - } - const conversation: INote[] = []; let i = 0; @@ -67,5 +73,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { await get(note.replyId); } - res(await packMany(conversation, user)); -})); + return await packMany(conversation, user); +}); diff --git a/src/server/api/endpoints/notes/create.ts b/src/server/api/endpoints/notes/create.ts index 47eebc186c..d24cd7b123 100644 --- a/src/server/api/endpoints/notes/create.ts +++ b/src/server/api/endpoints/notes/create.ts @@ -8,6 +8,7 @@ import DriveFile, { IDriveFile } from '../../../../models/drive-file'; import create from '../../../../services/note/create'; import define from '../../define'; import fetchMeta from '../../../../misc/fetch-meta'; +import { ApiError } from '../../error'; let maxNoteTextLength = 1000; @@ -180,10 +181,42 @@ export const meta = { } } } + }, + + errors: { + noSuchRenoteTarget: { + message: 'No such renote target.', + code: 'NO_SUCH_RENOTE_TARGET', + id: 'b5c90186-4ab0-49c8-9bba-a1f76c282ba4' + }, + + cannotReRenote: { + message: 'You can not Renote a pure Renote.', + code: 'CANNOT_RENOTE_TO_A_PURE_RENOTE', + id: 'fd4cc33e-2a37-48dd-99cc-9b806eb2031a' + }, + + noSuchReplyTarget: { + message: 'No such reply target.', + code: 'NO_SUCH_REPLY_TARGET', + id: '749ee0f6-d3da-459a-bf02-282e2da4292c' + }, + + cannotReplyToPureRenote: { + message: 'You can not reply to a pure Renote.', + code: 'CANNOT_REPLY_TO_A_PURE_RENOTE', + id: '3ac74a84-8fd5-4bb0-870f-01804f82ce15' + }, + + contentRequired: { + message: 'Content required. You need to set text, fileIds, renoteId or poll.', + code: 'CONTENT_REQUIRED', + id: '6f57e42b-c348-439b-bc45-993995cc515a' + }, } }; -export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user, app) => { let visibleUsers: IUser[] = []; if (ps.visibleUserIds) { visibleUsers = await Promise.all(ps.visibleUserIds.map(id => User.findOne({ @@ -212,9 +245,9 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { }); if (renote == null) { - return rej('renoteee is not found'); + throw new ApiError(meta.errors.noSuchRenoteTarget); } else if (renote.renoteId && !renote.text && !renote.fileIds) { - return rej('cannot renote to renote'); + throw new ApiError(meta.errors.cannotReRenote); } } @@ -226,12 +259,12 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { }); if (reply === null) { - return rej('in reply to note is not found'); + throw new ApiError(meta.errors.noSuchReplyTarget); } // 返信対象が引用でないRenoteだったらエラー if (reply.renoteId && !reply.text && !reply.fileIds) { - return rej('cannot reply to renote'); + throw new ApiError(meta.errors.cannotReplyToPureRenote); } } @@ -245,7 +278,7 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { // テキストが無いかつ添付ファイルが無いかつRenoteも無いかつ投票も無かったらエラー if (!(ps.text || files.length || renote || ps.poll)) { - return rej('text, fileIds, renoteId or poll is required'); + throw new ApiError(meta.errors.contentRequired); } // 後方互換性のため @@ -254,7 +287,7 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { } // 投稿を作成 - create(user, { + const note = await create(user, { createdAt: new Date(), files: files, poll: ps.poll, @@ -271,14 +304,9 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { apHashtags: ps.noExtractHashtags ? [] : undefined, apEmojis: ps.noExtractEmojis ? [] : undefined, geo: ps.geo - }) - .then(note => pack(note, user)) - .then(noteObj => { - res({ - createdNote: noteObj - }); - }) - .catch(e => { - rej(e); }); -})); + + return { + createdNote: await pack(note, user) + }; +}); diff --git a/src/server/api/endpoints/notes/delete.ts b/src/server/api/endpoints/notes/delete.ts index 261c7ad547..42f7494184 100644 --- a/src/server/api/endpoints/notes/delete.ts +++ b/src/server/api/endpoints/notes/delete.ts @@ -1,10 +1,11 @@ import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; -import Note from '../../../../models/note'; import deleteNote from '../../../../services/note/delete'; import User from '../../../../models/user'; import define from '../../define'; import * as ms from 'ms'; +import { getValiedNote } from '../../common/getters'; +import { ApiError } from '../../error'; export const meta = { stability: 'stable', @@ -33,24 +34,32 @@ export const meta = { 'en-US': 'Target note ID.' } } + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '490be23f-8c1f-4796-819f-94cb4f9d1630' + }, + + accessDenied: { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: 'fe8d7103-0ea8-4ec3-814d-f8b401dc69e9' + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // Fetch note - const note = await Note.findOne({ - _id: ps.noteId +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; }); - if (note === null) { - return rej('note not found'); - } - if (!user.isAdmin && !user.isModerator && !note.userId.equals(user._id)) { - return rej('access denied'); + throw new ApiError(meta.errors.accessDenied); } await deleteNote(await User.findOne({ _id: note.userId }), note); - - res(); -})); +}); diff --git a/src/server/api/endpoints/notes/favorites/create.ts b/src/server/api/endpoints/notes/favorites/create.ts index 8ec0891a31..bdb4aff72d 100644 --- a/src/server/api/endpoints/notes/favorites/create.ts +++ b/src/server/api/endpoints/notes/favorites/create.ts @@ -3,6 +3,7 @@ import ID, { transform } from '../../../../../misc/cafy-id'; import Favorite from '../../../../../models/favorite'; import Note from '../../../../../models/note'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -25,17 +26,31 @@ export const meta = { 'en-US': 'Target note ID.' } } + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '6dd26674-e060-4816-909a-45ba3f4da458' + }, + + alreadyFavorited: { + message: 'The note has already been marked as a favorite.', + code: 'ALREADY_FAVORITED', + id: 'a402c12b-34dd-41d2-97d8-4d2ffd96a1a6' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Get favoritee const note = await Note.findOne({ _id: ps.noteId }); if (note === null) { - return rej('note not found'); + throw new ApiError(meta.errors.noSuchNote); } // if already favorited @@ -45,7 +60,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (exist !== null) { - return rej('already favorited'); + throw new ApiError(meta.errors.alreadyFavorited); } // Create favorite @@ -55,6 +70,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { userId: user._id }); - // Send response - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/notes/favorites/delete.ts b/src/server/api/endpoints/notes/favorites/delete.ts index f0b9e249f7..700eafdaf7 100644 --- a/src/server/api/endpoints/notes/favorites/delete.ts +++ b/src/server/api/endpoints/notes/favorites/delete.ts @@ -3,6 +3,7 @@ import ID, { transform } from '../../../../../misc/cafy-id'; import Favorite from '../../../../../models/favorite'; import Note from '../../../../../models/note'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -25,17 +26,31 @@ export const meta = { 'en-US': 'Target note ID.' } } + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '80848a2c-398f-4343-baa9-df1d57696c56' + }, + + notFavorited: { + message: 'You have not marked that note a favorite.', + code: 'NOT_FAVORITED', + id: 'b625fc69-635e-45e9-86f4-dbefbef35af5' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Get favoritee const note = await Note.findOne({ _id: ps.noteId }); if (note === null) { - return rej('note not found'); + throw new ApiError(meta.errors.noSuchNote); } // if already favorited @@ -45,7 +60,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (exist === null) { - return rej('already not favorited'); + throw new ApiError(meta.errors.notFavorited); } // Delete favorite @@ -53,6 +68,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { _id: exist._id }); - // Send response - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/notes/featured.ts b/src/server/api/endpoints/notes/featured.ts index 7a3c2b76ee..08cc677abd 100644 --- a/src/server/api/endpoints/notes/featured.ts +++ b/src/server/api/endpoints/notes/featured.ts @@ -23,29 +23,28 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const day = 1000 * 60 * 60 * 24 * 2; const hideUserIds = await getHideUserIds(user); - const notes = await Note - .find({ - createdAt: { - $gt: new Date(Date.now() - day) - }, - deletedAt: null, - visibility: { $in: ['public', 'home'] }, - '_user.host': null, - ...(hideUserIds && hideUserIds.length > 0 ? { userId: { $nin: hideUserIds } } : {}) - }, { - limit: ps.limit, - sort: { - score: -1 - }, - hint: { - score: -1 - } - }); + const notes = await Note.find({ + createdAt: { + $gt: new Date(Date.now() - day) + }, + deletedAt: null, + visibility: { $in: ['public', 'home'] }, + '_user.host': null, + ...(hideUserIds && hideUserIds.length > 0 ? { userId: { $nin: hideUserIds } } : {}) + }, { + limit: ps.limit, + sort: { + score: -1 + }, + hint: { + score: -1 + } + }); - res(await packMany(notes, user)); -})); + return await packMany(notes, user); +}); diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts index 11825f288f..8670f8edbb 100644 --- a/src/server/api/endpoints/notes/global-timeline.ts +++ b/src/server/api/endpoints/notes/global-timeline.ts @@ -3,9 +3,9 @@ import ID, { transform } from '../../../../misc/cafy-id'; import Note from '../../../../models/note'; import { packMany } from '../../../../models/note'; import define from '../../define'; -import { countIf } from '../../../../prelude/array'; import fetchMeta from '../../../../misc/fetch-meta'; import { getHideUserIds } from '../../common/get-hide-users'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -49,22 +49,25 @@ export const meta = { untilDate: { validator: $.optional.num }, + }, + + errors: { + gtlDisabled: { + message: 'Global timeline has been disabled.', + code: 'GTL_DISABLED', + id: '0332fc13-6ab2-4427-ae80-a9fadffd1a6b' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - const meta = await fetchMeta(); - if (meta.disableGlobalTimeline) { +export default define(meta, async (ps, user) => { + const m = await fetchMeta(); + if (m.disableGlobalTimeline) { if (user == null || (!user.isAdmin && !user.isModerator)) { - return rej('global timeline disabled'); + throw new ApiError(meta.errors.gtlDisabled); } } - // Check if only one of sinceId, untilId, sinceDate, untilDate specified - if (countIf(x => x != null, [ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate]) > 1) { - return rej('only one of sinceId, untilId, sinceDate, untilDate can be specified'); - } - // 隠すユーザーを取得 const hideUserIds = await getHideUserIds(user); @@ -123,11 +126,10 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } //#endregion - const timeline = await Note - .find(query, { - limit: ps.limit, - sort: sort - }); + const timeline = await Note.find(query, { + limit: ps.limit, + sort: sort + }); - res(await packMany(timeline, user)); -})); + return await packMany(timeline, user); +}); diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts index 77ccf1abca..f3050caeca 100644 --- a/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -4,10 +4,10 @@ import Note from '../../../../models/note'; import { getFriends } from '../../common/get-friends'; import { packMany } from '../../../../models/note'; import define from '../../define'; -import { countIf } from '../../../../prelude/array'; import fetchMeta from '../../../../misc/fetch-meta'; import activeUsersChart from '../../../../services/chart/active-users'; import { getHideUserIds } from '../../common/get-hide-users'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -90,18 +90,21 @@ export const meta = { 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)' } }, - } -}; + }, -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - const meta = await fetchMeta(); - if (meta.disableLocalTimeline && !user.isAdmin && !user.isModerator) { - return rej('local timeline disabled'); + errors: { + stlDisabled: { + message: 'Social timeline has been disabled.', + code: 'STL_DISABLED', + id: '620763f4-f621-4533-ab33-0577a1a3c342' + }, } +}; - // Check if only one of sinceId, untilId, sinceDate, untilDate specified - if (countIf(x => x != null, [ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate]) > 1) { - return rej('only one of sinceId, untilId, sinceDate, untilDate can be specified'); +export default define(meta, async (ps, user) => { + const m = await fetchMeta(); + if (m.disableLocalTimeline && !user.isAdmin && !user.isModerator) { + throw new ApiError(meta.errors.stlDisabled); } const [followings, hideUserIds] = await Promise.all([ @@ -266,13 +269,12 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } //#endregion - const timeline = await Note - .find(query, { - limit: ps.limit, - sort: sort - }); - - res(await packMany(timeline, user)); + const timeline = await Note.find(query, { + limit: ps.limit, + sort: sort + }); activeUsersChart.update(user); -})); + + return await packMany(timeline, user); +}); diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts index 34695e1bca..44328ebede 100644 --- a/src/server/api/endpoints/notes/local-timeline.ts +++ b/src/server/api/endpoints/notes/local-timeline.ts @@ -3,10 +3,10 @@ import ID, { transform } from '../../../../misc/cafy-id'; import Note from '../../../../models/note'; import { packMany } from '../../../../models/note'; import define from '../../define'; -import { countIf } from '../../../../prelude/array'; import fetchMeta from '../../../../misc/fetch-meta'; import activeUsersChart from '../../../../services/chart/active-users'; import { getHideUserIds } from '../../common/get-hide-users'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -65,22 +65,25 @@ export const meta = { untilDate: { validator: $.optional.num, }, + }, + + errors: { + ltlDisabled: { + message: 'Local timeline has been disabled.', + code: 'LTL_DISABLED', + id: '45a6eb02-7695-4393-b023-dd3be9aaaefd' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - const meta = await fetchMeta(); - if (meta.disableLocalTimeline) { +export default define(meta, async (ps, user) => { + const m = await fetchMeta(); + if (m.disableLocalTimeline) { if (user == null || (!user.isAdmin && !user.isModerator)) { - return rej('local timeline disabled'); + throw new ApiError(meta.errors.ltlDisabled); } } - // Check if only one of sinceId, untilId, sinceDate, untilDate specified - if (countIf(x => x != null, [ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate]) > 1) { - return rej('only one of sinceId, untilId, sinceDate, untilDate can be specified'); - } - // 隠すユーザーを取得 const hideUserIds = await getHideUserIds(user); @@ -157,15 +160,14 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } //#endregion - const timeline = await Note - .find(query, { - limit: ps.limit, - sort: sort - }); - - res(await packMany(timeline, user)); + const timeline = await Note.find(query, { + limit: ps.limit, + sort: sort + }); if (user) { activeUsersChart.update(user); } -})); + + return await packMany(timeline, user); +}); diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index 3935a93599..090f184f7b 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -42,12 +42,7 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // Check if both of sinceId and untilId is specified - if (ps.sinceId && ps.untilId) { - return rej('cannot set sinceId and untilId'); - } - +export default define(meta, async (ps, user) => { // フォローを取得 const followings = await getFriends(user._id); @@ -131,15 +126,14 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }; } - const mentions = await Note - .find(query, { - limit: ps.limit, - sort: sort - }); - - res(await packMany(mentions, user)); + const mentions = await Note.find(query, { + limit: ps.limit, + sort: sort + }); for (const note of mentions) { read(user._id, note._id); } -})); + + return await packMany(mentions, user); +}); diff --git a/src/server/api/endpoints/notes/polls/recommendation.ts b/src/server/api/endpoints/notes/polls/recommendation.ts index 8e11e65296..61a1840b88 100644 --- a/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/src/server/api/endpoints/notes/polls/recommendation.ts @@ -25,7 +25,7 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Get votes const votes = await Vote.find({ userId: user._id @@ -41,29 +41,28 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { // 隠すユーザーを取得 const hideUserIds = await getHideUserIds(user); - const notes = await Note - .find({ - '_user.host': null, - _id: { - $nin: nin - }, - userId: { - $ne: user._id, - $nin: hideUserIds - }, - poll: { - $exists: true, - $ne: null - } - }, { - limit: ps.limit, - skip: ps.offset, - sort: { - _id: -1 - } - }); + const notes = await Note.find({ + '_user.host': null, + _id: { + $nin: nin + }, + userId: { + $ne: user._id, + $nin: hideUserIds + }, + poll: { + $exists: true, + $ne: null + } + }, { + limit: ps.limit, + skip: ps.offset, + sort: { + _id: -1 + } + }); - res(await Promise.all(notes.map(note => pack(note, user, { + return await Promise.all(notes.map(note => pack(note, user, { detail: true - })))); -})); + }))); +}); diff --git a/src/server/api/endpoints/notes/polls/vote.ts b/src/server/api/endpoints/notes/polls/vote.ts index 68c3898e05..fe0fffec61 100644 --- a/src/server/api/endpoints/notes/polls/vote.ts +++ b/src/server/api/endpoints/notes/polls/vote.ts @@ -9,6 +9,7 @@ import notify from '../../../../../services/create-notification'; import define from '../../../define'; import createNote from '../../../../../services/note/create'; import User from '../../../../../models/user'; +import { ApiError } from '../../../error'; export const meta = { desc: { @@ -33,24 +34,52 @@ export const meta = { choice: { validator: $.num }, + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: 'ecafbd2e-c283-4d6d-aecb-1a0a33b75396' + }, + + noPoll: { + message: 'The note does not attach a poll.', + code: 'NO_POLL', + id: '5f979967-52d9-4314-a911-1c673727f92f' + }, + + invalidChoice: { + message: 'Choice ID is invalid.', + code: 'INVALID_CHOICE', + id: 'e0cc9a04-f2e8-41e4-a5f1-4127293260cc' + }, + + alreadyVoted: { + message: 'You have already voted.', + code: 'ALREADY_VOTED', + id: '0963fc77-efac-419b-9424-b391608dc6d8' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Get votee const note = await Note.findOne({ _id: ps.noteId }); if (note === null) { - return rej('note not found'); + throw new ApiError(meta.errors.noSuchNote); } if (note.poll == null) { - return rej('poll not found'); + throw new ApiError(meta.errors.noPoll); } - if (!note.poll.choices.some(x => x.id == ps.choice)) return rej('invalid choice param'); + if (!note.poll.choices.some(x => x.id == ps.choice)) { + throw new ApiError(meta.errors.invalidChoice); + } // if already voted const exist = await Vote.findOne({ @@ -59,7 +88,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (exist !== null) { - return rej('already voted'); + throw new ApiError(meta.errors.alreadyVoted); } // Create vote @@ -70,9 +99,6 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { choice: ps.choice }); - // Send response - res(); - const inc: any = {}; inc[`poll.choices.${note.poll.choices.findIndex(c => c.id == ps.choice)}.votes`] = 1; @@ -132,4 +158,6 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { visibleUsers: [ pollOwner ], }); } -})); + + return; +}); diff --git a/src/server/api/endpoints/notes/reactions.ts b/src/server/api/endpoints/notes/reactions.ts index 54cbd23379..3e0656c4f8 100644 --- a/src/server/api/endpoints/notes/reactions.ts +++ b/src/server/api/endpoints/notes/reactions.ts @@ -1,8 +1,9 @@ import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; -import Note from '../../../../models/note'; import Reaction, { pack } from '../../../../models/note-reaction'; import define from '../../define'; +import { getValiedNote } from '../../common/getters'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -41,24 +42,23 @@ export const meta = { validator: $.optional.type(ID), transform: transform, }, - } -}; + }, -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // Check if both of sinceId and untilId is specified - if (ps.sinceId && ps.untilId) { - return rej('cannot set sinceId and untilId'); + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '263fff3d-d0e1-4af4-bea7-8408059b451a' + } } +}; - // Lookup note - const note = await Note.findOne({ - _id: ps.noteId +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; }); - if (note === null) { - return rej('note not found'); - } - const query = { noteId: note._id } as any; @@ -78,13 +78,11 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }; } - const reactions = await Reaction - .find(query, { - limit: ps.limit, - skip: ps.offset, - sort: sort - }); + const reactions = await Reaction.find(query, { + limit: ps.limit, + skip: ps.offset, + sort: sort + }); - // Serialize - res(await Promise.all(reactions.map(reaction => pack(reaction, user)))); -})); + return await Promise.all(reactions.map(reaction => pack(reaction, user))); +}); diff --git a/src/server/api/endpoints/notes/reactions/create.ts b/src/server/api/endpoints/notes/reactions/create.ts index 2612b129d0..65232e231d 100644 --- a/src/server/api/endpoints/notes/reactions/create.ts +++ b/src/server/api/endpoints/notes/reactions/create.ts @@ -1,11 +1,10 @@ -import * as mongo from 'mongodb'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import createReaction from '../../../../../services/note/reaction/create'; import { validateReaction } from '../../../../../models/note-reaction'; import define from '../../../define'; -import { IUser } from '../../../../../models/user'; import { getValiedNote } from '../../../common/getters'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -34,15 +33,38 @@ export const meta = { 'ja-JP': 'リアクションの種類' } } + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '033d0620-5bfe-4027-965d-980b0c85a3ea' + }, + + isMyNote: { + message: 'You can not react to your own notes.', + code: 'IS_MY_NOTE', + id: '7eeb9714-b047-43b5-b559-7b1b72810f53' + }, + + alreadyReacted: { + message: 'You are already reacting to that note.', + code: 'ALREADY_REACTED', + id: '71efcf98-86d6-4e2b-b2ad-9d032369366b' + } } }; -export default define(meta, (ps, user) => new Promise((res, rej) => { - createReactionById(user, ps.noteId, ps.reaction) - .then(r => res(r)).catch(e => rej(e)); -})); - -async function createReactionById(user: IUser, noteId: mongo.ObjectID, reaction: string) { - const note = await getValiedNote(noteId); - await createReaction(user, note, reaction); -} +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); + await createReaction(user, note, ps.reaction).catch(e => { + if (e.id === '2d8e7297-1873-4c00-8404-792c68d7bef0') throw new ApiError(meta.errors.isMyNote); + if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') throw new ApiError(meta.errors.alreadyReacted); + throw e; + }); + return; +}); diff --git a/src/server/api/endpoints/notes/reactions/delete.ts b/src/server/api/endpoints/notes/reactions/delete.ts index da3c2e3567..47e98f63e4 100644 --- a/src/server/api/endpoints/notes/reactions/delete.ts +++ b/src/server/api/endpoints/notes/reactions/delete.ts @@ -1,11 +1,10 @@ -import * as mongo from 'mongodb'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import define from '../../../define'; import * as ms from 'ms'; import deleteReaction from '../../../../../services/note/reaction/delete'; -import { IUser } from '../../../../../models/user'; import { getValiedNote } from '../../../common/getters'; +import { ApiError } from '../../../error'; export const meta = { desc: { @@ -32,15 +31,30 @@ export const meta = { 'en-US': 'Target note ID' } }, + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '764d9fce-f9f2-4a0e-92b1-6ceac9a7ad37' + }, + + notReacted: { + message: 'You are not reacting to that note.', + code: 'NOT_REACTED', + id: '92f4426d-4196-4125-aa5b-02943e2ec8fc' + }, } }; -export default define(meta, (ps, user) => new Promise((res, rej) => { - deleteReactionById(user, ps.noteId) - .then(r => res(r)).catch(e => rej(e)); -})); - -async function deleteReactionById(user: IUser, noteId: mongo.ObjectID) { - const note = await getValiedNote(noteId); - await deleteReaction(user, note); -} +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); + await deleteReaction(user, note).catch(e => { + if (e.id === '60527ec9-b4cb-4a88-a6bd-32d3ad26817d') throw new ApiError(meta.errors.notReacted); + throw e; + }); +}); diff --git a/src/server/api/endpoints/notes/renotes.ts b/src/server/api/endpoints/notes/renotes.ts index 531953540f..9873f1bffa 100644 --- a/src/server/api/endpoints/notes/renotes.ts +++ b/src/server/api/endpoints/notes/renotes.ts @@ -2,6 +2,8 @@ import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; import Note, { packMany } from '../../../../models/note'; import define from '../../define'; +import { getValiedNote } from '../../common/getters'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -35,24 +37,23 @@ export const meta = { validator: $.optional.type(ID), transform: transform, } - } -}; + }, -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // Check if both of sinceId and untilId is specified - if (ps.sinceId && ps.untilId) { - return rej('cannot set sinceId and untilId'); + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '12908022-2e21-46cd-ba6a-3edaf6093f46' + } } +}; - // Lookup note - const note = await Note.findOne({ - _id: ps.noteId +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; }); - if (note === null) { - return rej('note not found'); - } - const sort = { _id: -1 }; @@ -72,11 +73,10 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }; } - const renotes = await Note - .find(query, { - limit: ps.limit, - sort: sort - }); + const renotes = await Note.find(query, { + limit: ps.limit, + sort: sort + }); - res(await packMany(renotes, user)); -})); + return await packMany(renotes, user); +}); diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts index 26e9c6c5c9..080917f6f6 100644 --- a/src/server/api/endpoints/notes/replies.ts +++ b/src/server/api/endpoints/notes/replies.ts @@ -35,7 +35,7 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const [followings, hideUserIds] = await Promise.all([ // フォローを取得 // Fetch following @@ -85,5 +85,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { skip: ps.offset }); - res(await packMany(notes, user)); -})); + return await packMany(notes, user); +}); diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts index e671e72504..3eec2e3189 100644 --- a/src/server/api/endpoints/notes/search.ts +++ b/src/server/api/endpoints/notes/search.ts @@ -4,7 +4,7 @@ import Note from '../../../../models/note'; import { packMany } from '../../../../models/note'; import es from '../../../../db/elasticsearch'; import define from '../../define'; -import { apiLogger } from '../../logger'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -28,13 +28,21 @@ export const meta = { validator: $.optional.num.min(0), default: 0 } + }, + + errors: { + searchingNotAvailable: { + message: 'Searching not available.', + code: 'SEARCHING_NOT_AVAILABLE', + id: '7ee9c119-16a1-479f-a6fd-6fab00ed946f' + } } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { - if (es == null) return rej('searching not available'); +export default define(meta, async (ps, me) => { + if (es == null) throw new ApiError(meta.errors.searchingNotAvailable); - es.search({ + const response = await es.search({ index: 'misskey', type: 'note', body: { @@ -51,29 +59,24 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { { _doc: 'desc' } ] } - }, async (error, response) => { - if (error) { - apiLogger.error(error); - return res(500); - } - - if (response.hits.total === 0) { - return res([]); - } + }); - const hits = response.hits.hits.map(hit => new mongo.ObjectID(hit._id)); + if (response.hits.total === 0) { + return []; + } - // Fetch found notes - const notes = await Note.find({ - _id: { - $in: hits - } - }, { - sort: { - _id: -1 - } - }); + const hits = response.hits.hits.map(hit => new mongo.ObjectID(hit._id)); - res(await packMany(notes, me)); + // Fetch found notes + const notes = await Note.find({ + _id: { + $in: hits + } + }, { + sort: { + _id: -1 + } }); -})); + + return await packMany(notes, me); +}); diff --git a/src/server/api/endpoints/notes/search_by_tag.ts b/src/server/api/endpoints/notes/search_by_tag.ts index 05b22bb9f0..a4fd1903ef 100644 --- a/src/server/api/endpoints/notes/search_by_tag.ts +++ b/src/server/api/endpoints/notes/search_by_tag.ts @@ -103,7 +103,7 @@ export const meta = { } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const visibleQuery = me == null ? [{ visibility: { $in: [ 'public', 'home' ] } }] : [{ @@ -317,15 +317,13 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { } // Search notes - const notes = await Note - .find(q, { - sort: { - _id: -1 - }, - limit: ps.limit, - skip: ps.offset - }); + const notes = await Note.find(q, { + sort: { + _id: -1 + }, + limit: ps.limit, + skip: ps.offset + }); - // Serialize - res(await packMany(notes, me)); -})); + return await packMany(notes, me); +}); diff --git a/src/server/api/endpoints/notes/show.ts b/src/server/api/endpoints/notes/show.ts index 41134a2202..2134fad953 100644 --- a/src/server/api/endpoints/notes/show.ts +++ b/src/server/api/endpoints/notes/show.ts @@ -1,7 +1,9 @@ import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; -import Note, { pack } from '../../../../models/note'; +import { pack } from '../../../../models/note'; import define from '../../define'; +import { getValiedNote } from '../../common/getters'; +import { ApiError } from '../../error'; export const meta = { stability: 'stable', @@ -22,21 +24,24 @@ export const meta = { 'en-US': 'Target note ID.' } } + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '24fcbfc6-2e37-42b6-8388-c29b3861a08d' + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // Get note - const note = await Note.findOne({ - _id: ps.noteId +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; }); - if (note === null) { - return rej('note not found'); - } - - // Serialize - res(await pack(note, user, { + return await pack(note, user, { detail: true - })); -})); + }); +}); diff --git a/src/server/api/endpoints/notes/state.ts b/src/server/api/endpoints/notes/state.ts index 7519241e0b..07e35480fd 100644 --- a/src/server/api/endpoints/notes/state.ts +++ b/src/server/api/endpoints/notes/state.ts @@ -26,7 +26,7 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const [favorite, watching] = await Promise.all([ Favorite.count({ userId: user._id, @@ -42,8 +42,8 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }) ]); - res({ + return { isFavorited: favorite !== 0, isWatching: watching !== 0 - }); -})); + }; +}); diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts index aff3ec8cb6..cb6900a10b 100644 --- a/src/server/api/endpoints/notes/timeline.ts +++ b/src/server/api/endpoints/notes/timeline.ts @@ -4,7 +4,6 @@ import Note from '../../../../models/note'; import { getFriends } from '../../common/get-friends'; import { packMany } from '../../../../models/note'; import define from '../../define'; -import { countIf } from '../../../../prelude/array'; import activeUsersChart from '../../../../services/chart/active-users'; import { getHideUserIds } from '../../common/get-hide-users'; @@ -95,13 +94,7 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // Check if only one of sinceId, untilId, sinceDate, untilDate specified - if (countIf(x => x != null, [ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate]) > 1) { - rej('only one of sinceId, untilId, sinceDate, untilDate can be specified'); - return; - } - +export default define(meta, async (ps, user) => { const [followings, hideUserIds] = await Promise.all([ // フォローを取得 // Fetch following @@ -255,15 +248,12 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } //#endregion - // Issue query - const timeline = await Note - .find(query, { - limit: ps.limit, - sort: sort - }); - - // Serialize - res(await packMany(timeline, user)); + const timeline = await Note.find(query, { + limit: ps.limit, + sort: sort + }); activeUsersChart.update(user); -})); + + return await packMany(timeline, user); +}); diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts index eaaa80c3a7..1969b924ad 100644 --- a/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/src/server/api/endpoints/notes/user-list-timeline.ts @@ -6,6 +6,7 @@ import UserList from '../../../../models/user-list'; import define from '../../define'; import { getFriends } from '../../common/get-friends'; import { getHideUserIds } from '../../common/get-hide-users'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -99,10 +100,18 @@ export const meta = { 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)' } }, + }, + + errors: { + noSuchList: { + message: 'No such list.', + code: 'NO_SUCH_LIST', + id: '8fb1fbd5-e476-4c37-9fb0-43d55b63a2ff' + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const [list, followings, hideUserIds] = await Promise.all([ // リストを取得 // Fetch the list @@ -120,13 +129,11 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { ]); if (list == null) { - rej('list not found'); - return; + throw new ApiError(meta.errors.noSuchList); } if (list.userIds.length == 0) { - res([]); - return; + return []; } //#region Construct query @@ -274,13 +281,10 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } //#endregion - // Issue query - const timeline = await Note - .find(query, { - limit: ps.limit, - sort: sort - }); + const timeline = await Note.find(query, { + limit: ps.limit, + sort: sort + }); - // Serialize - res(await packMany(timeline, user)); -})); + return await packMany(timeline, user); +}); diff --git a/src/server/api/endpoints/notes/watching/create.ts b/src/server/api/endpoints/notes/watching/create.ts index 5efe69a3b4..ebba211fc0 100644 --- a/src/server/api/endpoints/notes/watching/create.ts +++ b/src/server/api/endpoints/notes/watching/create.ts @@ -1,8 +1,9 @@ import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; -import Note from '../../../../../models/note'; import define from '../../../define'; import watch from '../../../../../services/note/watch'; +import { getValiedNote } from '../../../common/getters'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -25,21 +26,22 @@ export const meta = { 'en-US': 'Target note ID.' } } + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: 'ea0e37a6-90a3-4f58-ba6b-c328ca206fc7' + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // Get note - const note = await Note.findOne({ - _id: ps.noteId +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; }); - if (note === null) { - return rej('note not found'); - } - await watch(user._id, note); - - // Send response - res(); -})); +}); diff --git a/src/server/api/endpoints/notes/watching/delete.ts b/src/server/api/endpoints/notes/watching/delete.ts index 2d99cc8254..63354d4ab2 100644 --- a/src/server/api/endpoints/notes/watching/delete.ts +++ b/src/server/api/endpoints/notes/watching/delete.ts @@ -1,8 +1,9 @@ import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; -import Note from '../../../../../models/note'; import define from '../../../define'; import unwatch from '../../../../../services/note/unwatch'; +import { getValiedNote } from '../../../common/getters'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -25,21 +26,22 @@ export const meta = { 'en-US': 'Target note ID.' } } + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '09b3695c-f72c-4731-a428-7cff825fc82e' + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // Get note - const note = await Note.findOne({ - _id: ps.noteId +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; }); - if (note === null) { - return rej('note not found'); - } - await unwatch(user._id, note); - - // Send response - res(); -})); +}); |