This commit is contained in:
syuilo 2017-12-15 00:23:45 +09:00
parent a357d5c6a5
commit d6ec5f2fe1
11 changed files with 305 additions and 80 deletions

View file

@ -48,7 +48,7 @@ if (isDebug) {
const constants = require('./src/const.json'); const constants = require('./src/const.json');
require('./src/web/docs/api/endpoints/gulpfile.ts'); require('./src/web/docs/api/gulpfile.ts');
gulp.task('build', [ gulp.task('build', [
'build:js', 'build:js',
@ -61,7 +61,7 @@ gulp.task('build', [
gulp.task('rebuild', ['clean', 'build']); gulp.task('rebuild', ['clean', 'build']);
gulp.task('build:doc', [ gulp.task('build:doc', [
'doc:endpoints', 'doc:api',
'doc:styles' 'doc:styles'
]); ]);

View file

@ -10,7 +10,7 @@ params:
optional: true optional: true
desc: desc:
ja: "投稿の本文" ja: "投稿の本文"
en: "Text of a post" en: "The text of your post"
- name: "media_ids" - name: "media_ids"
type: "id(DriveFile)[]" type: "id(DriveFile)[]"
optional: true optional: true
@ -22,19 +22,19 @@ params:
optional: true optional: true
desc: desc:
ja: "返信する投稿" ja: "返信する投稿"
en: "A post you want to reply" en: "The post you want to reply"
- name: "repost_id" - name: "repost_id"
type: "id(Post)" type: "id(Post)"
optional: true optional: true
desc: desc:
ja: "引用する投稿" ja: "引用する投稿"
en: "A post you want to quote" en: "The post you want to quote"
- name: "poll" - name: "poll"
type: "object" type: "object"
optional: true optional: true
desc: desc:
ja: "投票" ja: "投票"
en: "A poll" en: "The poll"
defName: "poll" defName: "poll"
def: def:
- name: "choices" - name: "choices"

View file

@ -1,4 +1,4 @@
@import "../../style" @import "../style"
#url #url
padding 8px 12px padding 8px 12px
@ -6,13 +6,3 @@
color #fff color #fff
background #222e40 background #222e40
border-radius 4px border-radius 4px
table
.name
font-weight bold
.name
.type
.optional
font-family Consolas, 'Courier New', Courier, Monaco, monospace

View file

@ -1,46 +1,13 @@
doctype html extends ../../layout.pug
include ../mixins
mixin i18n(xs) block title
each text, lang in xs | #{endpoint} | Misskey API
span(class=`i18n ${lang}`)= text
mixin table(params) block meta
table
thead: tr
th Name
th Type
th Optional
th Description
tbody
each param in params
tr
td.name= param.name
td.type
if param.kind == 'id'
| #{param.type} (
a(href=`/docs/api/entities/${param.entity}`)= param.entity
| ID)
else if param.kind == 'entity'
| #{param.type} (
a(href=`/docs/api/entities/${param.entity}`)= param.entity
| )
else if param.kind == 'object'
| #{param.type} (
a(href=`#${param.defName}`)= param.defName
| )
else
= param.type
td.optional= param.optional.toString()
td.desc: +i18n(param.desc)
html
head
meta(charset="UTF-8")
title #{endpoint} | Misskey API
link(rel="stylesheet" href="/assets/docs/api/endpoints/style.css") link(rel="stylesheet" href="/assets/docs/api/endpoints/style.css")
body block main
main
h1= endpoint h1= endpoint
p#url= url p#url= url
@ -49,15 +16,15 @@ html
section section
h2 Params h2 Params
+table(params) +propTable(params)
if paramDefs if paramDefs
each paramDef in paramDefs each paramDef in paramDefs
section(id= paramDef.name) section(id= paramDef.name)
h3= paramDef.name h3= paramDef.name
+table(paramDef.params) +propTable(paramDef.params)
section section
h2 Response h2 Response
+table(res) +propTable(res)

View file

@ -0,0 +1,124 @@
name: "Post"
desc:
ja: "投稿。"
en: "A post."
props:
- name: "id"
type: "id"
optional: false
desc:
ja: "投稿ID"
en: "The ID of this post"
- name: "created_at"
type: "date"
optional: false
desc:
ja: "投稿日時"
en: "The posted date of this post"
- name: "text"
type: "string"
optional: true
desc:
ja: "投稿の本文"
en: "The text of this post"
- name: "media_ids"
type: "id(DriveFile)[]"
optional: true
desc:
ja: "添付されているメディアのID"
en: "The IDs of the attached media"
- name: "media"
type: "entity(DriveFile)[]"
optional: true
desc:
ja: "添付されているメディア"
en: "The attached media"
- name: "user_id"
type: "id(User)"
optional: false
desc:
ja: "投稿者ID"
en: "The ID of author of this post"
- name: "user"
type: "entity(User)"
optional: true
desc:
ja: "投稿者"
en: "The author of this post"
- name: "my_reaction"
type: "string"
optional: true
desc:
ja: "この投稿に対する自分の<a href='/docs/api/reactions'>リアクション</a>"
en: "The your <a href='/docs/api/reactions'>reaction</a> of this post"
- name: "reaction_counts"
type: "object"
optional: false
desc:
ja: "<a href='/docs/api/reactions'>リアクション</a>をキーとし、この投稿に対するそのリアクションの数を値としたオブジェクト"
- name: "reply_id"
type: "id(Post)"
optional: true
desc:
ja: "返信した投稿のID"
en: "The ID of the replyed post"
- name: "reply"
type: "entity(Post)"
optional: true
desc:
ja: "返信した投稿"
en: "The replyed post"
- name: "repost_id"
type: "id(Post)"
optional: true
desc:
ja: "引用した投稿のID"
en: "The ID of the quoted post"
- name: "repost"
type: "entity(Post)"
optional: true
desc:
ja: "引用した投稿"
en: "The quoted post"
- name: "poll"
type: "object"
optional: true
desc:
ja: "投票"
en: "The poll"
defName: "poll"
def:
- name: "choices"
type: "object[]"
optional: false
desc:
ja: "投票の選択肢"
en: "The choices of this poll"
defName: "choice"
def:
- name: "id"
type: "number"
optional: false
desc:
ja: "選択肢ID"
en: "The ID of this choice"
- name: "is_voted"
type: "boolean"
optional: true
desc:
ja: "自分がこの選択肢に投票したかどうか"
en: "Whether you voted to this choice"
- name: "text"
type: "string"
optional: false
desc:
ja: "選択肢本文"
en: "The text of this choice"
- name: "votes"
type: "number"
optional: false
desc:
ja: "この選択肢に投票された数"
en: "The number voted for this choice"

View file

@ -0,0 +1 @@
@import "../style"

View file

@ -0,0 +1,23 @@
extends ../../layout.pug
include ../mixins
block title
| #{name} | Misskey API
block meta
link(rel="stylesheet" href="/assets/docs/api/entities/style.css")
block main
h1= name
p#desc: +i18n(desc)
section
h2 Properties
+propTable(props)
if propDefs
each propDef in propDefs
section(id= propDef.name)
h3= propDef.name
+propTable(propDef.params)

View file

@ -10,12 +10,15 @@ import * as pug from 'pug';
import * as yaml from 'js-yaml'; import * as yaml from 'js-yaml';
import * as mkdirp from 'mkdirp'; import * as mkdirp from 'mkdirp';
import config from './../../../../conf'; import config from './../../../conf';
const kebab = string => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase();
const parseParam = param => { const parseParam = param => {
const id = param.type.match(/^id\((.+?)\)/); const id = param.type.match(/^id\((.+?)\)|^id/);
const entity = param.type.match(/^entity\((.+?)\)/); const entity = param.type.match(/^entity\((.+?)\)/);
const isObject = /^object/.test(param.type); const isObject = /^object/.test(param.type);
const isDate = /^date/.test(param.type);
const isArray = /\[\]$/.test(param.type); const isArray = /\[\]$/.test(param.type);
if (id) { if (id) {
param.kind = 'id'; param.kind = 'id';
@ -36,30 +39,53 @@ const parseParam = param => {
if (isObject) { if (isObject) {
param.kind = 'object'; param.kind = 'object';
} }
if (isDate) {
param.kind = 'date';
param.type = 'string';
if (isArray) {
param.type += '[]';
}
}
return param; return param;
}; };
const sortParams = params => {
params.sort((a, b) => {
if (a.name < b.name)
return -1;
if (a.name > b.name)
return 1;
return 0;
});
return params;
};
const extractDefs = params => { const extractDefs = params => {
const defs = []; let defs = [];
params.forEach(param => { params.forEach(param => {
if (param.def) { if (param.def) {
defs.push({ defs.push({
name: param.defName, name: param.defName,
params: param.def.map(p => parseParam(p)) params: sortParams(param.def.map(p => parseParam(p)))
}); });
const childDefs = extractDefs(param.def); const childDefs = extractDefs(param.def);
defs.concat(childDefs); defs = defs.concat(childDefs);
} }
}); });
return defs; return defs;
}; };
gulp.task('doc:endpoints', () => { gulp.task('doc:api', [
'doc:api:endpoints',
'doc:api:entities'
]);
gulp.task('doc:api:endpoints', () => {
glob('./src/web/docs/api/endpoints/**/*.yaml', (globErr, files) => { glob('./src/web/docs/api/endpoints/**/*.yaml', (globErr, files) => {
if (globErr) { if (globErr) {
console.error(globErr); console.error(globErr);
@ -72,10 +98,11 @@ gulp.task('doc:endpoints', () => {
endpoint: ep.endpoint, endpoint: ep.endpoint,
url: `${config.api_url}/${ep.endpoint}`, url: `${config.api_url}/${ep.endpoint}`,
desc: ep.desc, desc: ep.desc,
params: ep.params.map(p => parseParam(p)), params: sortParams(ep.params.map(p => parseParam(p))),
paramDefs: extractDefs(ep.params), paramDefs: extractDefs(ep.params),
res: ep.res.map(p => parseParam(p)), res: sortParams(ep.res.map(p => parseParam(p))),
resDefs: extractDefs(ep.res) resDefs: extractDefs(ep.res),
kebab
}; };
pug.renderFile('./src/web/docs/api/endpoints/view.pug', vars, (renderErr, html) => { pug.renderFile('./src/web/docs/api/endpoints/view.pug', vars, (renderErr, html) => {
if (renderErr) { if (renderErr) {
@ -94,3 +121,36 @@ gulp.task('doc:endpoints', () => {
}); });
}); });
}); });
gulp.task('doc:api:entities', () => {
glob('./src/web/docs/api/entities/**/*.yaml', (globErr, files) => {
if (globErr) {
console.error(globErr);
return;
}
files.forEach(file => {
const entity = yaml.safeLoad(fs.readFileSync(file, 'utf-8'));
const vars = {
name: entity.name,
desc: entity.desc,
props: sortParams(entity.props.map(p => parseParam(p))),
propDefs: extractDefs(entity.props),
kebab
};
pug.renderFile('./src/web/docs/api/entities/view.pug', vars, (renderErr, html) => {
if (renderErr) {
console.error(renderErr);
return;
}
const htmlPath = `./built/web/docs/api/entities/${kebab(entity.name)}.html`;
mkdirp(path.dirname(htmlPath), (mkdirErr) => {
if (mkdirErr) {
console.error(mkdirErr);
return;
}
fs.writeFileSync(htmlPath, html, 'utf-8');
});
});
});
});
});

View file

@ -0,0 +1,33 @@
mixin propTable(props)
table.props
thead: tr
th Name
th Type
th Optional
th Description
tbody
each prop in props
tr
td.name= prop.name
td.type
i= prop.type
if prop.kind == 'id'
if prop.entity
| (
a(href=`/docs/api/entities/${kebab(prop.entity)}`)= prop.entity
| ID)
else
| (ID)
else if prop.kind == 'entity'
| (
a(href=`/docs/api/entities/${kebab(prop.entity)}`)= prop.entity
| )
else if prop.kind == 'object'
if prop.def
| (
a(href=`#${prop.defName}`)= prop.defName
| )
else if prop.kind == 'date'
| (Date)
td.optional= prop.optional.toString()
td.desc: +i18n(prop.desc)

View file

@ -0,0 +1,11 @@
@import "../style"
table.props
.name
font-weight bold
.name
.type
.optional
font-family Consolas, 'Courier New', Courier, Monaco, monospace

16
src/web/docs/layout.pug Normal file
View file

@ -0,0 +1,16 @@
doctype html
mixin i18n(xs)
each text, lang in xs
span(class=`i18n ${lang}`)!= text
html
head
meta(charset="UTF-8")
title
block title
block meta
body
main
block main