From 0463c6bb0f8fd32740ceb61ccce04c662272a618 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 23 Apr 2019 22:35:26 +0900 Subject: Refactor API (#4770) * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * Update description.ts * wip --- src/server/api/openapi/description.ts | 14 +- src/server/api/openapi/gen-spec.ts | 7 +- src/server/api/openapi/schemas.ts | 455 ++++------------------------------ 3 files changed, 58 insertions(+), 418 deletions(-) (limited to 'src/server/api/openapi') diff --git a/src/server/api/openapi/description.ts b/src/server/api/openapi/description.ts index 0a4c8699d1..9006dfbfa4 100644 --- a/src/server/api/openapi/description.ts +++ b/src/server/api/openapi/description.ts @@ -44,20 +44,20 @@ export function getDescription(lang = 'ja-JP'): string { const descriptions = { 'ja-JP': `**Misskey is a decentralized microblogging platform.** -## Usage +# Usage **APIはすべてPOSTでリクエスト/レスポンスともにJSON形式です。** 一部のAPIはリクエストに認証情報(APIキー)が必要です。リクエストの際に\`i\`というパラメータでAPIキーを添付してください。 -### 自分のアカウントのAPIキーを取得する +## 自分のアカウントのAPIキーを取得する 「設定 > API」で、自分のAPIキーを取得できます。 > アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。 -### アプリケーションとしてAPIキーを取得する +## アプリケーションとしてAPIキーを取得する 直接ユーザーのAPIキーをアプリケーションが扱うのはセキュリティ上のリスクがあるので、 アプリケーションからAPIを利用する際には、アプリケーションとアプリケーションを利用するユーザーが結び付けられた専用のAPIキーを発行します。 -#### 1.アプリケーションを登録する +### 1.アプリケーションを登録する まず、あなたのアプリケーションやWebサービス(以後、あなたのアプリと呼びます)をMisskeyに登録します。 [デベロッパーセンター](/dev)にアクセスし、「アプリ > アプリ作成」からアプリを作成してください。 @@ -65,7 +65,7 @@ export function getDescription(lang = 'ja-JP'): string { > アプリに成りすまされる可能性があるため、極力このシークレットキーは公開しないようにしてください。

-#### 2.ユーザーに認証させる +### 2.ユーザーに認証させる アプリを使ってもらうには、ユーザーにアカウントへのアクセスの許可をもらう必要があります。 認証セッションを開始するには、[${config.apiUrl}/auth/session/generate](#operation/auth/session/generate) へパラメータに\`appSecret\`としてシークレットキーを含めたリクエストを送信します。 @@ -76,7 +76,7 @@ export function getDescription(lang = 'ja-JP'): string { あなたのアプリがコールバックURLを設定していない場合、ユーザーがあなたのアプリの連携を許可したことを(何らかの方法で(たとえばボタンを押させるなど))確認出来るようにしてください。 -#### 3.アクセストークンを取得する +### 3.アクセストークンを取得する ユーザーが連携を許可したら、[${config.apiUrl}/auth/session/userkey](#operation/auth/session/userkey) へリクエストを送信します。 上手くいけば、認証したユーザーのアクセストークンがレスポンスとして取得できます。おめでとうございます! @@ -88,7 +88,7 @@ APIキーの生成方法を擬似コードで表すと次のようになりま const i = sha256(userToken + secretKey); \`\`\` -## Permissions +# Permissions |Permisson (kind)|Description|Endpoints| |:--|:--|:--| ${permissionTable} diff --git a/src/server/api/openapi/gen-spec.ts b/src/server/api/openapi/gen-spec.ts index d194c6c8a8..de26b970ea 100644 --- a/src/server/api/openapi/gen-spec.ts +++ b/src/server/api/openapi/gen-spec.ts @@ -2,9 +2,8 @@ import endpoints from '../endpoints'; import { Context } from 'cafy'; import config from '../../../config'; import { errors as basicErrors } from './errors'; -import { schemas } from './schemas'; +import { schemas, convertSchemaToOpenApiSchema } from './schemas'; import { getDescription } from './description'; -import { convertOpenApiSchema } from '../../../misc/schema'; export function genOpenapiSpec(lang = 'ja-JP') { const spec = { @@ -59,7 +58,7 @@ export function genOpenapiSpec(lang = 'ja-JP') { deprecated: (param.data || {}).deprecated, ...((param.data || {}).default ? { default: (param.data || {}).default } : {}), type: param.name === 'ID' ? 'string' : param.name.toLowerCase(), - ...(param.name === 'ID' ? { example: 'xxxxxxxxxxxxxxxxxxxxxxxx', format: 'id' } : {}), + ...(param.name === 'ID' ? { example: 'xxxxxxxxxx', format: 'id' } : {}), nullable: param.isNullable, ...(param.name === 'String' ? { ...((param as any).enum ? { enum: (param as any).enum } : {}), @@ -106,7 +105,7 @@ export function genOpenapiSpec(lang = 'ja-JP') { const required = endpoint.meta.params ? Object.entries(endpoint.meta.params).filter(([k, v]) => !v.validator.isOptional).map(([k, v]) => k) : []; - const resSchema = endpoint.meta.res ? convertOpenApiSchema(endpoint.meta.res) : {}; + const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res) : {}; let desc = (endpoint.meta.desc ? endpoint.meta.desc[lang] : 'No description provided.') + '\n\n'; desc += `**Credential required**: *${endpoint.meta.requireCredential ? 'Yes' : 'No'}*`; diff --git a/src/server/api/openapi/schemas.ts b/src/server/api/openapi/schemas.ts index 65826d9321..e54f989e74 100644 --- a/src/server/api/openapi/schemas.ts +++ b/src/server/api/openapi/schemas.ts @@ -1,3 +1,39 @@ +import { packedUserSchema } from '../../../models/repositories/user'; +import { Schema } from '../../../misc/schema'; +import { packedNoteSchema } from '../../../models/repositories/note'; +import { packedUserListSchema } from '../../../models/repositories/user-list'; +import { packedAppSchema } from '../../../models/repositories/app'; +import { packedMessagingMessageSchema } from '../../../models/repositories/messaging-message'; +import { packedNotificationSchema } from '../../../models/repositories/notification'; +import { packedDriveFileSchema } from '../../../models/repositories/drive-file'; +import { packedDriveFolderSchema } from '../../../models/repositories/drive-folder'; +import { packedFollowingSchema } from '../../../models/repositories/following'; +import { packedMutingSchema } from '../../../models/repositories/muting'; +import { packedBlockingSchema } from '../../../models/repositories/blocking'; +import { packedNoteReactionSchema } from '../../../models/repositories/note-reaction'; + +export function convertSchemaToOpenApiSchema(schema: Schema) { + const res: any = schema; + + if (schema.type === 'object' && schema.properties) { + res.required = Object.entries(schema.properties).filter(([k, v]) => v.optional !== true).map(([k]) => k); + + for (const k of Object.keys(schema.properties)) { + res.properties[k] = convertSchemaToOpenApiSchema(schema.properties[k]); + } + } + + if (schema.type === 'array' && schema.items) { + res.items = convertSchemaToOpenApiSchema(schema.items); + } + + if (schema.ref) { + res.$ref = `#/components/schemas/${schema.ref}`; + } + + return res; +} + export const schemas = { Error: { type: 'object', @@ -26,413 +62,18 @@ export const schemas = { required: ['error'] }, - User: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this User.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - username: { - type: 'string', - description: 'The screen name, handle, or alias that this user identifies themselves with.', - example: 'ai' - }, - name: { - type: 'string', - nullable: true, - description: 'The name of the user, as they’ve defined it.', - example: '藍' - }, - host: { - type: 'string', - nullable: true, - example: 'misskey.example.com' - }, - description: { - type: 'string', - nullable: true, - description: 'The user-defined UTF-8 string describing their account.', - example: 'Hi masters, I am Ai!' - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the user account was created on Misskey.' - }, - followersCount: { - type: 'number', - description: 'The number of followers this account currently has.' - }, - followingCount: { - type: 'number', - description: 'The number of users this account is following.' - }, - notesCount: { - type: 'number', - description: 'The number of Notes (including renotes) issued by the user.' - }, - isBot: { - type: 'boolean', - description: 'Whether this account is a bot.' - }, - isCat: { - type: 'boolean', - description: 'Whether this account is a cat.' - }, - isAdmin: { - type: 'boolean', - description: 'Whether this account is the admin.' - }, - isVerified: { - type: 'boolean' - }, - isLocked: { - type: 'boolean' - }, - }, - required: ['id', 'name', 'username', 'createdAt'] - }, - - UserList: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this UserList.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the UserList was created.' - }, - title: { - type: 'string', - description: 'The name of the UserList.' - }, - }, - required: ['id', 'createdAt', 'title'] - }, - - MessagingMessage: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this MessagingMessage.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the MessagingMessage was created.' - }, - text: { - type: 'string', - nullable: true - }, - file: { - type: 'DriveFile', - nullable: true - }, - recipientId: { - type: 'string', - format: 'id', - }, - recipient: { - $ref: '#/components/schemas/User' - }, - }, - required: ['id', 'createdAt'] - }, - - Note: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this Note.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the Note was created on Misskey.' - }, - text: { - type: 'string' - }, - cw: { - type: 'string' - }, - userId: { - type: 'string', - format: 'id', - }, - user: { - $ref: '#/components/schemas/User' - }, - replyId: { - type: 'string', - format: 'id', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - renoteId: { - type: 'string', - format: 'id', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - reply: { - $ref: '#/components/schemas/Note' - }, - renote: { - $ref: '#/components/schemas/Note' - }, - viaMobile: { - type: 'boolean' - }, - visibility: { - type: 'string' - }, - }, - required: ['id', 'userId', 'createdAt'] - }, - - Notification: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this notification.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the notification was created.' - }, - type: { - type: 'string', - enum: ['follow', 'receiveFollowRequest', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote'], - description: 'The type of the notification.' - }, - }, - required: ['id', 'createdAt', 'type'] - }, - - DriveFile: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this Drive file.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the Drive file was created on Misskey.' - }, - name: { - type: 'string', - description: 'The file name with extension.', - example: 'lenna.jpg' - }, - type: { - type: 'string', - description: 'The MIME type of this Drive file.', - example: 'image/jpeg' - }, - md5: { - type: 'string', - format: 'md5', - description: 'The MD5 hash of this Drive file.', - example: '15eca7fba0480996e2245f5185bf39f2' - }, - size: { - type: 'number', - description: 'The size of this Drive file. (bytes)', - example: 51469 - }, - folderId: { - type: 'string', - format: 'id', - nullable: true, - description: 'The parent folder ID of this Drive file.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - isSensitive: { - type: 'boolean', - description: 'Whether this Drive file is sensitive.', - }, - }, - required: ['id', 'createdAt', 'name', 'type', 'size', 'md5'] - }, - - DriveFolder: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this Drive folder.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the Drive folder was created.' - }, - name: { - type: 'string', - description: 'The folder name.', - }, - foldersCount: { - type: 'number', - description: 'The count of child folders.', - }, - filesCount: { - type: 'number', - description: 'The count of child files.', - }, - parentId: { - type: 'string', - format: 'id', - nullable: true, - description: 'The parent folder ID of this folder.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - parent: { - $ref: '#/components/schemas/DriveFolder' - }, - }, - required: ['id', 'createdAt', 'name'] - }, - - Following: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this following.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the following was created.' - }, - followeeId: { - type: 'string', - format: 'id', - }, - followee: { - $ref: '#/components/schemas/User', - description: 'The followee.' - }, - followerId: { - type: 'string', - format: 'id', - }, - follower: { - $ref: '#/components/schemas/User', - description: 'The follower.' - }, - }, - required: ['id', 'createdAt', 'followeeId', 'followerId'] - }, - - Muting: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this mute.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the mute was created.' - }, - mutee: { - $ref: '#/components/schemas/User', - description: 'The mutee.' - }, - }, - required: ['id', 'createdAt', 'mutee'] - }, - - Blocking: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this block.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the block was created.' - }, - blockee: { - $ref: '#/components/schemas/User', - description: 'The blockee.' - }, - }, - required: ['id', 'createdAt', 'blockee'] - }, - - Reaction: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this reaction.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the reaction was created.' - }, - user: { - $ref: '#/components/schemas/User', - description: 'User who performed this reaction.' - }, - type: { - type: 'string', - enum: [ - 'like', - 'love', - 'laugh', - 'hmm', - 'surprise', - 'congrats', - 'angry', - 'confused', - 'rip', - 'pudding', - 'star' - ], - description: 'The reaction type.' - }, - }, - required: ['id', 'createdAt', 'user', 'type'] - }, + User: convertSchemaToOpenApiSchema(packedUserSchema), + UserList: convertSchemaToOpenApiSchema(packedUserListSchema), + App: convertSchemaToOpenApiSchema(packedAppSchema), + MessagingMessage: convertSchemaToOpenApiSchema(packedMessagingMessageSchema), + Note: convertSchemaToOpenApiSchema(packedNoteSchema), + Notification: convertSchemaToOpenApiSchema(packedNotificationSchema), + DriveFile: convertSchemaToOpenApiSchema(packedDriveFileSchema), + DriveFolder: convertSchemaToOpenApiSchema(packedDriveFolderSchema), + Following: convertSchemaToOpenApiSchema(packedFollowingSchema), + Muting: convertSchemaToOpenApiSchema(packedMutingSchema), + Blocking: convertSchemaToOpenApiSchema(packedBlockingSchema), + NoteReaction: convertSchemaToOpenApiSchema(packedNoteReactionSchema), Hashtag: { type: 'object', -- cgit v1.2.3-freya