fix(backend): enumerate achievement types in some response schema (#15953)
* fix(backend): enumerate achievement types in some response schema * refactor: use `ref`
This commit is contained in:
parent
09317150e1
commit
3b676f39df
|
@ -9,87 +9,7 @@ import type { MiUser } from '@/models/User.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { NotificationService } from '@/core/NotificationService.js';
|
import { NotificationService } from '@/core/NotificationService.js';
|
||||||
|
import { ACHIEVEMENT_TYPES } from '@/models/UserProfile.js';
|
||||||
export const ACHIEVEMENT_TYPES = [
|
|
||||||
'notes1',
|
|
||||||
'notes10',
|
|
||||||
'notes100',
|
|
||||||
'notes500',
|
|
||||||
'notes1000',
|
|
||||||
'notes5000',
|
|
||||||
'notes10000',
|
|
||||||
'notes20000',
|
|
||||||
'notes30000',
|
|
||||||
'notes40000',
|
|
||||||
'notes50000',
|
|
||||||
'notes60000',
|
|
||||||
'notes70000',
|
|
||||||
'notes80000',
|
|
||||||
'notes90000',
|
|
||||||
'notes100000',
|
|
||||||
'login3',
|
|
||||||
'login7',
|
|
||||||
'login15',
|
|
||||||
'login30',
|
|
||||||
'login60',
|
|
||||||
'login100',
|
|
||||||
'login200',
|
|
||||||
'login300',
|
|
||||||
'login400',
|
|
||||||
'login500',
|
|
||||||
'login600',
|
|
||||||
'login700',
|
|
||||||
'login800',
|
|
||||||
'login900',
|
|
||||||
'login1000',
|
|
||||||
'passedSinceAccountCreated1',
|
|
||||||
'passedSinceAccountCreated2',
|
|
||||||
'passedSinceAccountCreated3',
|
|
||||||
'loggedInOnBirthday',
|
|
||||||
'loggedInOnNewYearsDay',
|
|
||||||
'noteClipped1',
|
|
||||||
'noteFavorited1',
|
|
||||||
'myNoteFavorited1',
|
|
||||||
'profileFilled',
|
|
||||||
'markedAsCat',
|
|
||||||
'following1',
|
|
||||||
'following10',
|
|
||||||
'following50',
|
|
||||||
'following100',
|
|
||||||
'following300',
|
|
||||||
'followers1',
|
|
||||||
'followers10',
|
|
||||||
'followers50',
|
|
||||||
'followers100',
|
|
||||||
'followers300',
|
|
||||||
'followers500',
|
|
||||||
'followers1000',
|
|
||||||
'collectAchievements30',
|
|
||||||
'viewAchievements3min',
|
|
||||||
'iLoveMisskey',
|
|
||||||
'foundTreasure',
|
|
||||||
'client30min',
|
|
||||||
'client60min',
|
|
||||||
'noteDeletedWithin1min',
|
|
||||||
'postedAtLateNight',
|
|
||||||
'postedAt0min0sec',
|
|
||||||
'selfQuote',
|
|
||||||
'htl20npm',
|
|
||||||
'viewInstanceChart',
|
|
||||||
'outputHelloWorldOnScratchpad',
|
|
||||||
'open3windows',
|
|
||||||
'driveFolderCircularReference',
|
|
||||||
'reactWithoutRead',
|
|
||||||
'clickedClickHere',
|
|
||||||
'justPlainLucky',
|
|
||||||
'setNameToSyuilo',
|
|
||||||
'cookieClicked',
|
|
||||||
'brainDiver',
|
|
||||||
'smashTestNotificationButton',
|
|
||||||
'tutorialCompleted',
|
|
||||||
'bubbleGameExplodingHead',
|
|
||||||
'bubbleGameDoubleExplodingHead',
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AchievementService {
|
export class AchievementService {
|
||||||
|
|
|
@ -67,6 +67,7 @@ import { packedChatMessageSchema, packedChatMessageLiteSchema, packedChatMessage
|
||||||
import { packedChatRoomSchema } from '@/models/json-schema/chat-room.js';
|
import { packedChatRoomSchema } from '@/models/json-schema/chat-room.js';
|
||||||
import { packedChatRoomInvitationSchema } from '@/models/json-schema/chat-room-invitation.js';
|
import { packedChatRoomInvitationSchema } from '@/models/json-schema/chat-room-invitation.js';
|
||||||
import { packedChatRoomMembershipSchema } from '@/models/json-schema/chat-room-membership.js';
|
import { packedChatRoomMembershipSchema } from '@/models/json-schema/chat-room-membership.js';
|
||||||
|
import { packedAchievementNameSchema, packedAchievementSchema } from '@/models/json-schema/achievement.js';
|
||||||
|
|
||||||
export const refs = {
|
export const refs = {
|
||||||
UserLite: packedUserLiteSchema,
|
UserLite: packedUserLiteSchema,
|
||||||
|
@ -78,6 +79,8 @@ export const refs = {
|
||||||
User: packedUserSchema,
|
User: packedUserSchema,
|
||||||
|
|
||||||
UserList: packedUserListSchema,
|
UserList: packedUserListSchema,
|
||||||
|
Achievement: packedAchievementSchema,
|
||||||
|
AchievementName: packedAchievementNameSchema,
|
||||||
Ad: packedAdSchema,
|
Ad: packedAdSchema,
|
||||||
Announcement: packedAnnouncementSchema,
|
Announcement: packedAnnouncementSchema,
|
||||||
App: packedAppSchema,
|
App: packedAppSchema,
|
||||||
|
|
|
@ -274,7 +274,7 @@ export class MiUserProfile {
|
||||||
default: [],
|
default: [],
|
||||||
})
|
})
|
||||||
public achievements: {
|
public achievements: {
|
||||||
name: string;
|
name: typeof ACHIEVEMENT_TYPES[number];
|
||||||
unlockedAt: number;
|
unlockedAt: number;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
|
@ -295,3 +295,84 @@ export class MiUserProfile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ACHIEVEMENT_TYPES = [
|
||||||
|
'notes1',
|
||||||
|
'notes10',
|
||||||
|
'notes100',
|
||||||
|
'notes500',
|
||||||
|
'notes1000',
|
||||||
|
'notes5000',
|
||||||
|
'notes10000',
|
||||||
|
'notes20000',
|
||||||
|
'notes30000',
|
||||||
|
'notes40000',
|
||||||
|
'notes50000',
|
||||||
|
'notes60000',
|
||||||
|
'notes70000',
|
||||||
|
'notes80000',
|
||||||
|
'notes90000',
|
||||||
|
'notes100000',
|
||||||
|
'login3',
|
||||||
|
'login7',
|
||||||
|
'login15',
|
||||||
|
'login30',
|
||||||
|
'login60',
|
||||||
|
'login100',
|
||||||
|
'login200',
|
||||||
|
'login300',
|
||||||
|
'login400',
|
||||||
|
'login500',
|
||||||
|
'login600',
|
||||||
|
'login700',
|
||||||
|
'login800',
|
||||||
|
'login900',
|
||||||
|
'login1000',
|
||||||
|
'passedSinceAccountCreated1',
|
||||||
|
'passedSinceAccountCreated2',
|
||||||
|
'passedSinceAccountCreated3',
|
||||||
|
'loggedInOnBirthday',
|
||||||
|
'loggedInOnNewYearsDay',
|
||||||
|
'noteClipped1',
|
||||||
|
'noteFavorited1',
|
||||||
|
'myNoteFavorited1',
|
||||||
|
'profileFilled',
|
||||||
|
'markedAsCat',
|
||||||
|
'following1',
|
||||||
|
'following10',
|
||||||
|
'following50',
|
||||||
|
'following100',
|
||||||
|
'following300',
|
||||||
|
'followers1',
|
||||||
|
'followers10',
|
||||||
|
'followers50',
|
||||||
|
'followers100',
|
||||||
|
'followers300',
|
||||||
|
'followers500',
|
||||||
|
'followers1000',
|
||||||
|
'collectAchievements30',
|
||||||
|
'viewAchievements3min',
|
||||||
|
'iLoveMisskey',
|
||||||
|
'foundTreasure',
|
||||||
|
'client30min',
|
||||||
|
'client60min',
|
||||||
|
'noteDeletedWithin1min',
|
||||||
|
'postedAtLateNight',
|
||||||
|
'postedAt0min0sec',
|
||||||
|
'selfQuote',
|
||||||
|
'htl20npm',
|
||||||
|
'viewInstanceChart',
|
||||||
|
'outputHelloWorldOnScratchpad',
|
||||||
|
'open3windows',
|
||||||
|
'driveFolderCircularReference',
|
||||||
|
'reactWithoutRead',
|
||||||
|
'clickedClickHere',
|
||||||
|
'justPlainLucky',
|
||||||
|
'setNameToSyuilo',
|
||||||
|
'cookieClicked',
|
||||||
|
'brainDiver',
|
||||||
|
'smashTestNotificationButton',
|
||||||
|
'tutorialCompleted',
|
||||||
|
'bubbleGameExplodingHead',
|
||||||
|
'bubbleGameDoubleExplodingHead',
|
||||||
|
] as const;
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ACHIEVEMENT_TYPES } from '@/models/UserProfile.js';
|
||||||
|
|
||||||
|
export const packedAchievementNameSchema = {
|
||||||
|
type: 'string',
|
||||||
|
enum: ACHIEVEMENT_TYPES,
|
||||||
|
optional: false,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const packedAchievementSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
ref: 'AchievementName',
|
||||||
|
},
|
||||||
|
unlockedAt: {
|
||||||
|
type: 'number',
|
||||||
|
optional: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
|
@ -3,7 +3,6 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ACHIEVEMENT_TYPES } from '@/core/AchievementService.js';
|
|
||||||
import { notificationTypes, userExportableEntities } from '@/types.js';
|
import { notificationTypes, userExportableEntities } from '@/types.js';
|
||||||
|
|
||||||
const baseSchema = {
|
const baseSchema = {
|
||||||
|
@ -312,9 +311,7 @@ export const packedNotificationSchema = {
|
||||||
enum: ['achievementEarned'],
|
enum: ['achievementEarned'],
|
||||||
},
|
},
|
||||||
achievement: {
|
achievement: {
|
||||||
type: 'string',
|
ref: 'AchievementName',
|
||||||
optional: false, nullable: false,
|
|
||||||
enum: ACHIEVEMENT_TYPES,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
|
|
|
@ -630,18 +630,7 @@ export const packedMeDetailedOnlySchema = {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
nullable: false, optional: false,
|
nullable: false, optional: false,
|
||||||
items: {
|
items: {
|
||||||
type: 'object',
|
ref: 'Achievement',
|
||||||
nullable: false, optional: false,
|
|
||||||
properties: {
|
|
||||||
name: {
|
|
||||||
type: 'string',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
unlockedAt: {
|
|
||||||
type: 'number',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
loggedInDays: {
|
loggedInDays: {
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import { AchievementService, ACHIEVEMENT_TYPES } from '@/core/AchievementService.js';
|
import { AchievementService } from '@/core/AchievementService.js';
|
||||||
|
import { ACHIEVEMENT_TYPES } from '@/models/UserProfile.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
|
@ -14,15 +14,7 @@ export const meta = {
|
||||||
res: {
|
res: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: {
|
||||||
type: 'object',
|
ref: 'Achievement',
|
||||||
properties: {
|
|
||||||
name: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
unlockedAt: {
|
|
||||||
type: 'number',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -232,7 +232,7 @@ describe('UserEntityService', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('MeDetailed', async() => {
|
test('MeDetailed', async() => {
|
||||||
const achievements = [{ name: 'achievement', unlockedAt: new Date().getTime() }];
|
const achievements = [{ name: 'iLoveMisskey' as const, unlockedAt: new Date().getTime() }];
|
||||||
const me = await createUser({}, {
|
const me = await createUser({}, {
|
||||||
birthday: '2000-01-01',
|
birthday: '2000-01-01',
|
||||||
achievements: achievements,
|
achievements: achievements,
|
||||||
|
|
|
@ -30,6 +30,12 @@ declare namespace acct {
|
||||||
}
|
}
|
||||||
export { acct }
|
export { acct }
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type Achievement = components['schemas']['Achievement'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type AchievementName = components['schemas']['AchievementName'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type Ad = components['schemas']['Ad'];
|
type Ad = components['schemas']['Ad'];
|
||||||
|
|
||||||
|
@ -2084,6 +2090,8 @@ declare namespace entities {
|
||||||
UserDetailed,
|
UserDetailed,
|
||||||
User,
|
User,
|
||||||
UserList,
|
UserList,
|
||||||
|
Achievement,
|
||||||
|
AchievementName,
|
||||||
Ad,
|
Ad,
|
||||||
Announcement,
|
Announcement,
|
||||||
App,
|
App,
|
||||||
|
|
|
@ -8,6 +8,8 @@ export type MeDetailed = components['schemas']['MeDetailed'];
|
||||||
export type UserDetailed = components['schemas']['UserDetailed'];
|
export type UserDetailed = components['schemas']['UserDetailed'];
|
||||||
export type User = components['schemas']['User'];
|
export type User = components['schemas']['User'];
|
||||||
export type UserList = components['schemas']['UserList'];
|
export type UserList = components['schemas']['UserList'];
|
||||||
|
export type Achievement = components['schemas']['Achievement'];
|
||||||
|
export type AchievementName = components['schemas']['AchievementName'];
|
||||||
export type Ad = components['schemas']['Ad'];
|
export type Ad = components['schemas']['Ad'];
|
||||||
export type Announcement = components['schemas']['Announcement'];
|
export type Announcement = components['schemas']['Announcement'];
|
||||||
export type App = components['schemas']['App'];
|
export type App = components['schemas']['App'];
|
||||||
|
|
|
@ -4303,10 +4303,7 @@ export type components = {
|
||||||
}]>;
|
}]>;
|
||||||
};
|
};
|
||||||
emailNotificationTypes: string[];
|
emailNotificationTypes: string[];
|
||||||
achievements: {
|
achievements: components['schemas']['Achievement'][];
|
||||||
name: string;
|
|
||||||
unlockedAt: number;
|
|
||||||
}[];
|
|
||||||
loggedInDays: number;
|
loggedInDays: number;
|
||||||
policies: components['schemas']['RolePolicies'];
|
policies: components['schemas']['RolePolicies'];
|
||||||
/** @default false */
|
/** @default false */
|
||||||
|
@ -4344,6 +4341,12 @@ export type components = {
|
||||||
userIds?: string[];
|
userIds?: string[];
|
||||||
isPublic: boolean;
|
isPublic: boolean;
|
||||||
};
|
};
|
||||||
|
Achievement: {
|
||||||
|
name: components['schemas']['AchievementName'];
|
||||||
|
unlockedAt: number;
|
||||||
|
};
|
||||||
|
/** @enum {string} */
|
||||||
|
AchievementName: 'notes1' | 'notes10' | 'notes100' | 'notes500' | 'notes1000' | 'notes5000' | 'notes10000' | 'notes20000' | 'notes30000' | 'notes40000' | 'notes50000' | 'notes60000' | 'notes70000' | 'notes80000' | 'notes90000' | 'notes100000' | 'login3' | 'login7' | 'login15' | 'login30' | 'login60' | 'login100' | 'login200' | 'login300' | 'login400' | 'login500' | 'login600' | 'login700' | 'login800' | 'login900' | 'login1000' | 'passedSinceAccountCreated1' | 'passedSinceAccountCreated2' | 'passedSinceAccountCreated3' | 'loggedInOnBirthday' | 'loggedInOnNewYearsDay' | 'noteClipped1' | 'noteFavorited1' | 'myNoteFavorited1' | 'profileFilled' | 'markedAsCat' | 'following1' | 'following10' | 'following50' | 'following100' | 'following300' | 'followers1' | 'followers10' | 'followers50' | 'followers100' | 'followers300' | 'followers500' | 'followers1000' | 'collectAchievements30' | 'viewAchievements3min' | 'iLoveMisskey' | 'foundTreasure' | 'client30min' | 'client60min' | 'noteDeletedWithin1min' | 'postedAtLateNight' | 'postedAt0min0sec' | 'selfQuote' | 'htl20npm' | 'viewInstanceChart' | 'outputHelloWorldOnScratchpad' | 'open3windows' | 'driveFolderCircularReference' | 'reactWithoutRead' | 'clickedClickHere' | 'justPlainLucky' | 'setNameToSyuilo' | 'cookieClicked' | 'brainDiver' | 'smashTestNotificationButton' | 'tutorialCompleted' | 'bubbleGameExplodingHead' | 'bubbleGameDoubleExplodingHead';
|
||||||
Ad: {
|
Ad: {
|
||||||
/**
|
/**
|
||||||
* Format: id
|
* Format: id
|
||||||
|
@ -4619,16 +4622,15 @@ export type components = {
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
type: 'chatRoomInvitationReceived';
|
type: 'chatRoomInvitationReceived';
|
||||||
invitation: components['schemas']['ChatRoomInvitation'];
|
invitation: components['schemas']['ChatRoomInvitation'];
|
||||||
} | ({
|
} | {
|
||||||
/** Format: id */
|
/** Format: id */
|
||||||
id: string;
|
id: string;
|
||||||
/** Format: date-time */
|
/** Format: date-time */
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
type: 'achievementEarned';
|
type: 'achievementEarned';
|
||||||
/** @enum {string} */
|
achievement: components['schemas']['AchievementName'];
|
||||||
achievement: 'notes1' | 'notes10' | 'notes100' | 'notes500' | 'notes1000' | 'notes5000' | 'notes10000' | 'notes20000' | 'notes30000' | 'notes40000' | 'notes50000' | 'notes60000' | 'notes70000' | 'notes80000' | 'notes90000' | 'notes100000' | 'login3' | 'login7' | 'login15' | 'login30' | 'login60' | 'login100' | 'login200' | 'login300' | 'login400' | 'login500' | 'login600' | 'login700' | 'login800' | 'login900' | 'login1000' | 'passedSinceAccountCreated1' | 'passedSinceAccountCreated2' | 'passedSinceAccountCreated3' | 'loggedInOnBirthday' | 'loggedInOnNewYearsDay' | 'noteClipped1' | 'noteFavorited1' | 'myNoteFavorited1' | 'profileFilled' | 'markedAsCat' | 'following1' | 'following10' | 'following50' | 'following100' | 'following300' | 'followers1' | 'followers10' | 'followers50' | 'followers100' | 'followers300' | 'followers500' | 'followers1000' | 'collectAchievements30' | 'viewAchievements3min' | 'iLoveMisskey' | 'foundTreasure' | 'client30min' | 'client60min' | 'noteDeletedWithin1min' | 'postedAtLateNight' | 'postedAt0min0sec' | 'selfQuote' | 'htl20npm' | 'viewInstanceChart' | 'outputHelloWorldOnScratchpad' | 'open3windows' | 'driveFolderCircularReference' | 'reactWithoutRead' | 'clickedClickHere' | 'justPlainLucky' | 'setNameToSyuilo' | 'cookieClicked' | 'brainDiver' | 'smashTestNotificationButton' | 'tutorialCompleted' | 'bubbleGameExplodingHead' | 'bubbleGameDoubleExplodingHead';
|
} | ({
|
||||||
}) | ({
|
|
||||||
/** Format: id */
|
/** Format: id */
|
||||||
id: string;
|
id: string;
|
||||||
/** Format: date-time */
|
/** Format: date-time */
|
||||||
|
@ -28531,10 +28533,7 @@ export type operations = {
|
||||||
/** @description OK (with results) */
|
/** @description OK (with results) */
|
||||||
200: {
|
200: {
|
||||||
content: {
|
content: {
|
||||||
'application/json': {
|
'application/json': components['schemas']['Achievement'][];
|
||||||
name: string;
|
|
||||||
unlockedAt: number;
|
|
||||||
}[];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
/** @description Client error */
|
/** @description Client error */
|
||||||
|
|
Loading…
Reference in New Issue