fix(backend): better notes/translate error response (#13631)
				
					
				
			* fix(backend): better `notes/translate` error response * Update CHANGELOG.md * test(backend): perform administrative operations as `root` --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									f3500ffda9
								
							
						
					
					
						commit
						b35ae97ba7
					
				
					 4 changed files with 97 additions and 28 deletions
				
			
		| 
						 | 
				
			
			@ -35,6 +35,7 @@
 | 
			
		|||
- Enhance: misskey-dev/summaly@5.1.0の取り込み(プレビュー生成処理の効率化)
 | 
			
		||||
- Fix: フォローリクエストを作成する際に既存のものは削除するように  
 | 
			
		||||
  (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/440)
 | 
			
		||||
- Fix: エンドポイント`notes/translate`のエラーを改善
 | 
			
		||||
- Fix: CleanRemoteFilesProcessorService report progress from 100% (#13632)
 | 
			
		||||
 | 
			
		||||
## 2024.3.1
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,7 @@ export const meta = {
 | 
			
		|||
 | 
			
		||||
	res: {
 | 
			
		||||
		type: 'object',
 | 
			
		||||
		optional: false, nullable: false,
 | 
			
		||||
		optional: true, nullable: false,
 | 
			
		||||
		properties: {
 | 
			
		||||
			sourceLang: { type: 'string' },
 | 
			
		||||
			text: { type: 'string' },
 | 
			
		||||
| 
						 | 
				
			
			@ -39,6 +39,11 @@ export const meta = {
 | 
			
		|||
			code: 'NO_SUCH_NOTE',
 | 
			
		||||
			id: 'bea9b03f-36e0-49c5-a4db-627a029f8971',
 | 
			
		||||
		},
 | 
			
		||||
		cannotTranslateInvisibleNote: {
 | 
			
		||||
			message: 'Cannot translate invisible note.',
 | 
			
		||||
			code: 'CANNOT_TRANSLATE_INVISIBLE_NOTE',
 | 
			
		||||
			id: 'ea29f2ca-c368-43b3-aaf1-5ac3e74bbe5d',
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
} as const;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -72,17 +77,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 | 
			
		|||
			});
 | 
			
		||||
 | 
			
		||||
			if (!(await this.noteEntityService.isVisibleForMe(note, me.id))) {
 | 
			
		||||
				return 204; // TODO: 良い感じのエラー返す
 | 
			
		||||
				throw new ApiError(meta.errors.cannotTranslateInvisibleNote);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (note.text == null) {
 | 
			
		||||
				return 204;
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			const instance = await this.metaService.fetch();
 | 
			
		||||
 | 
			
		||||
			if (instance.deeplAuthKey == null) {
 | 
			
		||||
				return 204; // TODO: 良い感じのエラー返す
 | 
			
		||||
				throw new ApiError(meta.errors.unavailable);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			let targetLang = ps.targetLang;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,12 +8,13 @@ process.env.NODE_ENV = 'test';
 | 
			
		|||
import * as assert from 'assert';
 | 
			
		||||
import { MiNote } from '@/models/Note.js';
 | 
			
		||||
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
 | 
			
		||||
import { api, initTestDb, post, signup, uploadFile, uploadUrl } from '../utils.js';
 | 
			
		||||
import { api, initTestDb, post, role, signup, uploadFile, uploadUrl } from '../utils.js';
 | 
			
		||||
import type * as misskey from 'misskey-js';
 | 
			
		||||
 | 
			
		||||
describe('Note', () => {
 | 
			
		||||
	let Notes: any;
 | 
			
		||||
 | 
			
		||||
	let root: misskey.entities.SignupResponse;
 | 
			
		||||
	let alice: misskey.entities.SignupResponse;
 | 
			
		||||
	let bob: misskey.entities.SignupResponse;
 | 
			
		||||
	let tom: misskey.entities.SignupResponse;
 | 
			
		||||
| 
						 | 
				
			
			@ -21,6 +22,7 @@ describe('Note', () => {
 | 
			
		|||
	beforeAll(async () => {
 | 
			
		||||
		const connection = await initTestDb(true);
 | 
			
		||||
		Notes = connection.getRepository(MiNote);
 | 
			
		||||
		root = await signup({ username: 'root' });
 | 
			
		||||
		alice = await signup({ username: 'alice' });
 | 
			
		||||
		bob = await signup({ username: 'bob' });
 | 
			
		||||
		tom = await signup({ username: 'tom', host: 'example.com' });
 | 
			
		||||
| 
						 | 
				
			
			@ -473,14 +475,14 @@ describe('Note', () => {
 | 
			
		|||
						value: true,
 | 
			
		||||
					},
 | 
			
		||||
				} as any,
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(res.status, 200);
 | 
			
		||||
 | 
			
		||||
			const assign = await api('admin/roles/assign', {
 | 
			
		||||
				userId: alice.id,
 | 
			
		||||
				roleId: res.body.id,
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(assign.status, 204);
 | 
			
		||||
			assert.strictEqual(file.body!.isSensitive, false);
 | 
			
		||||
| 
						 | 
				
			
			@ -508,11 +510,11 @@ describe('Note', () => {
 | 
			
		|||
			await api('admin/roles/unassign', {
 | 
			
		||||
				userId: alice.id,
 | 
			
		||||
				roleId: res.body.id,
 | 
			
		||||
			});
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			await api('admin/roles/delete', {
 | 
			
		||||
				roleId: res.body.id,
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -644,7 +646,7 @@ describe('Note', () => {
 | 
			
		|||
				sensitiveWords: [
 | 
			
		||||
					'test',
 | 
			
		||||
				],
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(sensitive.status, 204);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -663,7 +665,7 @@ describe('Note', () => {
 | 
			
		|||
				sensitiveWords: [
 | 
			
		||||
					'/Test/i',
 | 
			
		||||
				],
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(sensitive.status, 204);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -680,7 +682,7 @@ describe('Note', () => {
 | 
			
		|||
				sensitiveWords: [
 | 
			
		||||
					'Test hoge',
 | 
			
		||||
				],
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(sensitive.status, 204);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -697,7 +699,7 @@ describe('Note', () => {
 | 
			
		|||
				prohibitedWords: [
 | 
			
		||||
					'test',
 | 
			
		||||
				],
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(prohibited.status, 204);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -716,7 +718,7 @@ describe('Note', () => {
 | 
			
		|||
				prohibitedWords: [
 | 
			
		||||
					'/Test/i',
 | 
			
		||||
				],
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(prohibited.status, 204);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -733,7 +735,7 @@ describe('Note', () => {
 | 
			
		|||
				prohibitedWords: [
 | 
			
		||||
					'Test hoge',
 | 
			
		||||
				],
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(prohibited.status, 204);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -750,7 +752,7 @@ describe('Note', () => {
 | 
			
		|||
				prohibitedWords: [
 | 
			
		||||
					'test',
 | 
			
		||||
				],
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(prohibited.status, 204);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -785,7 +787,7 @@ describe('Note', () => {
 | 
			
		|||
						value: 0,
 | 
			
		||||
					},
 | 
			
		||||
				} as any,
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(res.status, 200);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -794,7 +796,7 @@ describe('Note', () => {
 | 
			
		|||
			const assign = await api('admin/roles/assign', {
 | 
			
		||||
				userId: alice.id,
 | 
			
		||||
				roleId: res.body.id,
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(assign.status, 204);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -810,11 +812,11 @@ describe('Note', () => {
 | 
			
		|||
			await api('admin/roles/unassign', {
 | 
			
		||||
				userId: alice.id,
 | 
			
		||||
				roleId: res.body.id,
 | 
			
		||||
			});
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			await api('admin/roles/delete', {
 | 
			
		||||
				roleId: res.body.id,
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		test('ダイレクト投稿もエラーになる', async () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -839,7 +841,7 @@ describe('Note', () => {
 | 
			
		|||
						value: 0,
 | 
			
		||||
					},
 | 
			
		||||
				} as any,
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(res.status, 200);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -848,7 +850,7 @@ describe('Note', () => {
 | 
			
		|||
			const assign = await api('admin/roles/assign', {
 | 
			
		||||
				userId: alice.id,
 | 
			
		||||
				roleId: res.body.id,
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(assign.status, 204);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -866,11 +868,11 @@ describe('Note', () => {
 | 
			
		|||
			await api('admin/roles/unassign', {
 | 
			
		||||
				userId: alice.id,
 | 
			
		||||
				roleId: res.body.id,
 | 
			
		||||
			});
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			await api('admin/roles/delete', {
 | 
			
		||||
				roleId: res.body.id,
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		test('ダイレクトの宛先とメンションが同じ場合は重複してカウントしない', async () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -895,7 +897,7 @@ describe('Note', () => {
 | 
			
		|||
						value: 1,
 | 
			
		||||
					},
 | 
			
		||||
				} as any,
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(res.status, 200);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -904,7 +906,7 @@ describe('Note', () => {
 | 
			
		|||
			const assign = await api('admin/roles/assign', {
 | 
			
		||||
				userId: alice.id,
 | 
			
		||||
				roleId: res.body.id,
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(assign.status, 204);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -921,11 +923,11 @@ describe('Note', () => {
 | 
			
		|||
			await api('admin/roles/unassign', {
 | 
			
		||||
				userId: alice.id,
 | 
			
		||||
				roleId: res.body.id,
 | 
			
		||||
			});
 | 
			
		||||
			}, root);
 | 
			
		||||
 | 
			
		||||
			await api('admin/roles/delete', {
 | 
			
		||||
				roleId: res.body.id,
 | 
			
		||||
			}, alice);
 | 
			
		||||
			}, root);
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -960,4 +962,61 @@ describe('Note', () => {
 | 
			
		|||
			assert.strictEqual(mainNote.repliesCount, 0);
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	describe('notes/translate', () => {
 | 
			
		||||
		describe('翻訳機能の利用が許可されていない場合', () => {
 | 
			
		||||
			let cannotTranslateRole: misskey.entities.Role;
 | 
			
		||||
 | 
			
		||||
			beforeAll(async () => {
 | 
			
		||||
				cannotTranslateRole = await role(root, {}, { canUseTranslator: false });
 | 
			
		||||
				await api('admin/roles/assign', { roleId: cannotTranslateRole.id, userId: alice.id }, root);
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			test('翻訳機能の利用が許可されていない場合翻訳できない', async () => {
 | 
			
		||||
				const aliceNote = await post(alice, { text: 'Hello' });
 | 
			
		||||
				const res = await api('notes/translate', {
 | 
			
		||||
					noteId: aliceNote.id,
 | 
			
		||||
					targetLang: 'ja',
 | 
			
		||||
				}, alice);
 | 
			
		||||
 | 
			
		||||
				assert.strictEqual(res.status, 400);
 | 
			
		||||
				assert.strictEqual(res.body.error.code, 'UNAVAILABLE');
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			afterAll(async () => {
 | 
			
		||||
				await api('admin/roles/unassign', { roleId: cannotTranslateRole.id, userId: alice.id }, root);
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		test('存在しないノートは翻訳できない', async () => {
 | 
			
		||||
			const res = await api('notes/translate', { noteId: 'foo', targetLang: 'ja' }, alice);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(res.status, 400);
 | 
			
		||||
			assert.strictEqual(res.body.error.code, 'NO_SUCH_NOTE');
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		test('不可視なノートは翻訳できない', async () => {
 | 
			
		||||
			const aliceNote = await post(alice, { visibility: 'followers', text: 'Hello' });
 | 
			
		||||
			const bobTranslateAttempt = await api('notes/translate', { noteId: aliceNote.id, targetLang: 'ja' }, bob);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(bobTranslateAttempt.status, 400);
 | 
			
		||||
			assert.strictEqual(bobTranslateAttempt.body.error.code, 'CANNOT_TRANSLATE_INVISIBLE_NOTE');
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		test('text: null なノートを翻訳すると空のレスポンスが返ってくる', async () => {
 | 
			
		||||
			const aliceNote = await post(alice, { text: null, poll: { choices: ['kinoko', 'takenoko'] } });
 | 
			
		||||
			const res = await api('notes/translate', { noteId: aliceNote.id, targetLang: 'ja' }, alice);
 | 
			
		||||
 | 
			
		||||
			assert.strictEqual(res.status, 204);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		test('サーバーに DeepL 認証キーが登録されていない場合翻訳できない', async () => {
 | 
			
		||||
			const aliceNote = await post(alice, { text: 'Hello' });
 | 
			
		||||
			const res = await api('notes/translate', { noteId: aliceNote.id, targetLang: 'ja' }, alice);
 | 
			
		||||
 | 
			
		||||
			// NOTE: デフォルトでは登録されていないので落ちる
 | 
			
		||||
			assert.strictEqual(res.status, 400);
 | 
			
		||||
			assert.strictEqual(res.body.error.code, 'UNAVAILABLE');
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21864,6 +21864,10 @@ export type operations = {
 | 
			
		|||
          };
 | 
			
		||||
        };
 | 
			
		||||
      };
 | 
			
		||||
      /** @description OK (without any results) */
 | 
			
		||||
      204: {
 | 
			
		||||
        content: never;
 | 
			
		||||
      };
 | 
			
		||||
      /** @description Client error */
 | 
			
		||||
      400: {
 | 
			
		||||
        content: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue