From 86a693b18235d96936808fc396b23ba09bf590d8 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 15 Oct 2024 14:03:57 -0400 Subject: [PATCH] factor out tuple logic into from-tuple.ts --- .../src/core/activitypub/ApDbResolverService.ts | 7 ++----- .../backend/src/core/activitypub/ApInboxService.ts | 11 ++++++----- .../src/core/activitypub/ApResolverService.ts | 3 ++- packages/backend/src/core/activitypub/type.ts | 4 +++- packages/backend/src/misc/from-tuple.ts | 7 +++++++ packages/backend/test/unit/misc/from-tuple.ts | 13 +++++++++++++ 6 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 packages/backend/src/misc/from-tuple.ts create mode 100644 packages/backend/test/unit/misc/from-tuple.ts diff --git a/packages/backend/src/core/activitypub/ApDbResolverService.ts b/packages/backend/src/core/activitypub/ApDbResolverService.ts index 6905f8bee9..2cb558dbff 100644 --- a/packages/backend/src/core/activitypub/ApDbResolverService.ts +++ b/packages/backend/src/core/activitypub/ApDbResolverService.ts @@ -59,7 +59,7 @@ export class ApDbResolverService implements OnApplicationShutdown { } @bindThis - public parseUri(value: string | IObject): UriParseResult { + public parseUri(value: string | IObject | [string | IObject]): UriParseResult { const separator = '/'; const uri = new URL(getApId(value)); @@ -78,7 +78,7 @@ export class ApDbResolverService implements OnApplicationShutdown { * AP Note => Misskey Note in DB */ @bindThis - public async getNoteFromApId(value: string | IObject): Promise { + public async getNoteFromApId(value: string | IObject | [string | IObject]): Promise { const parsed = this.parseUri(value); if (parsed.local) { @@ -99,9 +99,6 @@ export class ApDbResolverService implements OnApplicationShutdown { */ @bindThis public async getUserFromApId(value: string | IObject | [string | IObject]): Promise { - // eslint-disable-next-line no-param-reassign - if (Array.isArray(value)) value = value[0]; - const parsed = this.parseUri(value); if (parsed.local) { diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts index 289f1606a1..fd4b3d8d6f 100644 --- a/packages/backend/src/core/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -41,6 +41,7 @@ import { ApPersonService } from './models/ApPersonService.js'; import { ApQuestionService } from './models/ApQuestionService.js'; import type { Resolver } from './ApResolverService.js'; import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IReject, IRemove, IUndo, IUpdate, IMove, IPost } from './type.js'; +import { fromTuple } from '@/misc/from-tuple.js'; @Injectable() export class ApInboxService { @@ -253,7 +254,7 @@ export class ApInboxService { } if (activity.target === actor.featured) { - const object = Array.isArray(activity.object) ? activity.object[0] : activity.object; + const object = fromTuple(activity.object); const note = await this.apNoteService.resolveNote(object); if (note == null) return 'note not found'; await this.notePiningService.addPinned(actor, note.id); @@ -271,7 +272,7 @@ export class ApInboxService { const resolver = this.apResolverService.createResolver(); - const activityObject = Array.isArray(activity.object) ? activity.object[0] : activity.object; + const activityObject = fromTuple(activity.object); if (!activityObject) return 'skip: activity has no object property'; const targetUri = getApId(activityObject); if (targetUri.startsWith('bear:')) return 'skip: bearcaps url not supported.'; @@ -372,7 +373,7 @@ export class ApInboxService { this.logger.info(`Create: ${uri}`); - const activityObject = Array.isArray(activity.object) ? activity.object[0] : activity.object; + const activityObject = fromTuple(activity.object); if (!activityObject) return 'skip: activity has no object property'; const targetUri = getApId(activityObject); if (targetUri.startsWith('bear:')) return 'skip: bearcaps url not supported.'; @@ -451,7 +452,7 @@ export class ApInboxService { // 削除対象objectのtype let formerType: string | undefined; - const activityObject = Array.isArray(activity.object) ? activity.object[0] : activity.object; + const activityObject = fromTuple(activity.object); if (typeof activityObject === 'string') { // typeが不明だけど、どうせ消えてるのでremote resolveしない formerType = undefined; @@ -619,7 +620,7 @@ export class ApInboxService { } if (activity.target === actor.featured) { - const activityObject = Array.isArray(activity.object) ? activity.object[0] : activity.object; + const activityObject = fromTuple(activity.object); const note = await this.apNoteService.resolveNote(activityObject); if (note == null) return 'note not found'; await this.notePiningService.removePinned(actor, note.id); diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts index 2a803d394d..fd69c7269b 100644 --- a/packages/backend/src/core/activitypub/ApResolverService.ts +++ b/packages/backend/src/core/activitypub/ApResolverService.ts @@ -21,6 +21,7 @@ import { ApDbResolverService } from './ApDbResolverService.js'; import { ApRendererService } from './ApRendererService.js'; import { ApRequestService } from './ApRequestService.js'; import type { IObject, ICollection, IOrderedCollection } from './type.js'; +import { fromTuple } from '@/misc/from-tuple.js'; export class Resolver { private history: Set; @@ -69,7 +70,7 @@ export class Resolver { @bindThis public async resolve(value: string | IObject | [string | IObject]): Promise { // eslint-disable-next-line no-param-reassign - if (Array.isArray(value)) value = value[0]; + value = fromTuple(value); if (typeof value !== 'string') { return value; diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts index 2ec3b0baff..144793c214 100644 --- a/packages/backend/src/core/activitypub/type.ts +++ b/packages/backend/src/core/activitypub/type.ts @@ -3,6 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { fromTuple } from '@/misc/from-tuple.js'; + export type Obj = { [x: string]: any }; export type ApObject = IObject | string | (IObject | string)[]; @@ -54,7 +56,7 @@ export function getOneApId(value: ApObject): string { */ export function getApId(value: string | IObject | [string | IObject]): string { // eslint-disable-next-line no-param-reassign - if (Array.isArray(value)) value = value[0]; + value = fromTuple(value); if (typeof value === 'string') return value; if (typeof value.id === 'string') return value.id; diff --git a/packages/backend/src/misc/from-tuple.ts b/packages/backend/src/misc/from-tuple.ts new file mode 100644 index 0000000000..366b1e310f --- /dev/null +++ b/packages/backend/src/misc/from-tuple.ts @@ -0,0 +1,7 @@ +export function fromTuple(value: T | [T]): T { + if (Array.isArray(value)) { + return value[0]; + } + + return value; +} diff --git a/packages/backend/test/unit/misc/from-tuple.ts b/packages/backend/test/unit/misc/from-tuple.ts new file mode 100644 index 0000000000..b523cb5782 --- /dev/null +++ b/packages/backend/test/unit/misc/from-tuple.ts @@ -0,0 +1,13 @@ +import { fromTuple } from '@/misc/from-tuple.js'; + +describe(fromTuple, () => { + it('should return value when value is not an array', () => { + const value = fromTuple('abc'); + expect(value).toBe('abc'); + }); + + it('should return first element when value is an array', () => { + const value = fromTuple(['abc']); + expect(value).toBe('abc'); + }); +});