Merge tag '2023.9.2' into merge-upstream

This commit is contained in:
riku6460 2023-09-29 22:16:04 +09:00
commit e2c9ed355b
No known key found for this signature in database
GPG key ID: 27414FA27DB94CF6
144 changed files with 2519 additions and 1660 deletions

View file

@ -34,10 +34,11 @@ describe('api:notes/create', () => {
.toBe(VALID);
});
test('null post', () => {
expect(v({ text: null }))
.toBe(INVALID);
});
// TODO
//test('null post', () => {
// expect(v({ text: null }))
// .toBe(INVALID);
//});
test('0 characters post', () => {
expect(v({ text: '' }))

View file

@ -119,7 +119,7 @@ export const paramDef = {
type: 'string',
minLength: 1,
maxLength: MAX_NOTE_TEXT_LENGTH,
nullable: false,
nullable: true,
},
fileIds: {
type: 'array',

View file

@ -4,6 +4,7 @@
*/
import { Inject, Injectable } from '@nestjs/common';
import { Brackets } from 'typeorm';
import type { NotesRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueryService } from '@/core/QueryService.js';
@ -40,6 +41,7 @@ export const paramDef = {
properties: {
withFiles: { type: 'boolean', default: false },
withReplies: { type: 'boolean', default: false },
withRenotes: { type: 'boolean', default: true },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
sinceId: { type: 'string', format: 'misskey:id' },
untilId: { type: 'string', format: 'misskey:id' },
@ -88,6 +90,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (ps.withFiles) {
query.andWhere('note.fileIds != \'{}\'');
}
if (ps.withRenotes === false) {
query.andWhere(new Brackets(qb => {
qb.orWhere('note.renoteId IS NULL');
qb.orWhere(new Brackets(qb => {
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
}));
}));
}
//#endregion
const timeline = await query.limit(ps.limit).getMany();

View file

@ -52,6 +52,7 @@ export const paramDef = {
includeLocalRenotes: { type: 'boolean', default: true },
withFiles: { type: 'boolean', default: false },
withReplies: { type: 'boolean', default: false },
withRenotes: { type: 'boolean', default: true },
},
required: [],
} as const;
@ -137,6 +138,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (ps.withFiles) {
query.andWhere('note.fileIds != \'{}\'');
}
if (ps.withRenotes === false) {
query.andWhere(new Brackets(qb => {
qb.orWhere('note.renoteId IS NULL');
qb.orWhere(new Brackets(qb => {
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
}));
}));
}
//#endregion
const timeline = await query.limit(ps.limit).getMany();

View file

@ -42,6 +42,7 @@ export const paramDef = {
properties: {
withFiles: { type: 'boolean', default: false },
withReplies: { type: 'boolean', default: false },
withRenotes: { type: 'boolean', default: true },
fileType: { type: 'array', items: {
type: 'string',
} },
@ -110,6 +111,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
query.andWhere('0 = (SELECT COUNT(*) FROM drive_file df WHERE df.id = ANY(note."fileIds") AND df."isSensitive" = TRUE)');
}
}
if (ps.withRenotes === false) {
query.andWhere(new Brackets(qb => {
qb.orWhere('note.renoteId IS NULL');
qb.orWhere(new Brackets(qb => {
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
}));
}));
}
//#endregion
const timeline = await query.limit(ps.limit).getMany();

View file

@ -42,6 +42,7 @@ export const paramDef = {
includeLocalRenotes: { type: 'boolean', default: true },
withFiles: { type: 'boolean', default: false },
withReplies: { type: 'boolean', default: false },
withRenotes: { type: 'boolean', default: true },
},
required: [],
} as const;
@ -126,6 +127,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (ps.withFiles) {
query.andWhere('note.fileIds != \'{}\'');
}
if (ps.withRenotes === false) {
query.andWhere(new Brackets(qb => {
qb.orWhere('note.renoteId IS NULL');
qb.orWhere(new Brackets(qb => {
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
}));
}));
}
//#endregion
const timeline = await query.limit(ps.limit).getMany();

View file

@ -0,0 +1,89 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import ms from 'ms';
import { Inject, Injectable } from '@nestjs/common';
import type { UsersRepository, NotesRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { NoteDeleteService } from '@/core/NoteDeleteService.js';
import { DI } from '@/di-symbols.js';
import { GetterService } from '@/server/api/GetterService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
import { ApiError } from '../../error.js';
export const meta = {
tags: ['notes'],
requireCredential: true,
requireRolePolicy: ['canUpdateContent', 'canEditNote'],
kind: 'write:notes',
limit: {
duration: ms('1hour'),
max: 10,
minInterval: ms('1sec'),
},
errors: {
noSuchNote: {
message: 'No such note.',
code: 'NO_SUCH_NOTE',
id: 'a6584e14-6e01-4ad3-b566-851e7bf0d474',
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
noteId: { type: 'string', format: 'misskey:id' },
text: {
type: 'string',
minLength: 1,
maxLength: MAX_NOTE_TEXT_LENGTH,
nullable: false,
},
cw: { type: 'string', nullable: true, maxLength: 100 },
},
required: ['noteId', 'text', 'cw'],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
private getterService: GetterService,
private globalEventService: GlobalEventService,
) {
super(meta, paramDef, async (ps, me) => {
const note = await this.getterService.getNote(ps.noteId).catch(err => {
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
throw err;
});
if (note.userId !== me.id) {
throw new ApiError(meta.errors.noSuchNote);
}
await this.notesRepository.update({ id: note.id }, {
updatedAt: new Date(),
cw: ps.cw,
text: ps.text,
});
this.globalEventService.publishNoteStream(note.id, 'updated', {
cw: ps.cw,
text: ps.text,
});
});
}
}

View file

@ -49,6 +49,8 @@ export const paramDef = {
includeMyRenotes: { type: 'boolean', default: true },
includeRenotedMyNotes: { type: 'boolean', default: true },
includeLocalRenotes: { type: 'boolean', default: true },
withReplies: { type: 'boolean', default: false },
withRenotes: { type: 'boolean', default: true },
withFiles: {
type: 'boolean',
default: false,
@ -130,6 +132,20 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}));
}
if (!ps.withReplies) {
query.andWhere('note.replyId IS NULL');
}
if (ps.withRenotes === false) {
query.andWhere(new Brackets(qb => {
qb.orWhere('note.renoteId IS NULL');
qb.orWhere(new Brackets(qb => {
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
}));
}));
}
if (ps.withFiles) {
query.andWhere('note.fileIds != \'{}\'');
}