UNPKG

6.5 kBJavaScriptView Raw
1"use strict"
2
3const _ = require("lodash")
4const jsonfile = require("jsonfile")
5const shell = require("shelljs")
6const ip = require("ip")
7const fs = require("fs")
8
9const debug = require("debug")("motif:swagger")
10
11class Swagger {
12 constructor(context) {
13 this._context = context
14 this._config = context.config.get("swagger")
15 }
16
17 isMultipartData(params) {
18 let isMultipartData = false
19 _.forEach(params, param => {
20 if (param.type === "file") {
21 isMultipartData = true
22 }
23 })
24 return
25 }
26
27 paramInfo(param, typeIn) {
28 const paramInfo = {
29 name: param.name,
30 in: typeIn,
31 description: param.description || "No description",
32 required: param.optional === true ? false : true,
33 type: param.type || "string"
34 // enums
35 }
36
37 if (_.isArray(param.enum)) {
38 paramInfo.enum = param.enum
39 }
40
41 return paramInfo
42 }
43
44 pathInfo(route, info, isAdmin = false) {
45 const responseType = "application/json"
46 const requestType = this.isMultipartData(route.params || []) ?
47 "multipart/form-data" :
48 "application/x-www-form-urlencoded"
49
50 const parameters = (route.params || []).map(param => {
51 const paramsTypeIn = ((route, param) => {
52 if (route.path.indexOf(":" + param.name) !== -1) {
53 return "path"
54 }
55
56 if (route.method === "put" || route.method === "post") {
57 return "formData"
58 }
59
60 return "query"
61 })(route, param)
62
63 debug("paramsTypeIn", route.path, param.name, paramsTypeIn)
64
65 return this.paramInfo(param, paramsTypeIn)
66 })
67
68 const tags = route.tags || [route.group]
69
70 const isAdminAPI =
71 tags.indexOf("admin") !== -1 ||
72 (route.permissions && route.permissions.indexOf("admin") !== -1)
73
74 if (!isAdmin && isAdminAPI) {
75 return null
76 } else if (isAdmin && !isAdminAPI) {
77 return null
78 }
79
80 const pathInfo = {
81 tags: _.filter(tags, tag => { return tag !== 'admin' }),
82 summary: route.summary || "",
83 description: route.description || "No description",
84 produces: [responseType],
85 consumes: [requestType],
86 parameters: parameters,
87 responses: {
88 "200": {
89 description: "A JSON result",
90 content: {
91 "application/json": {
92 schema: {
93 $ref: "#/definitions/BasicResult"
94 }
95 }
96 }
97 }
98 }
99 }
100
101 if (route.permissions) {
102 pathInfo["security"] = [{ APIKeyHeader: [] }]
103 }
104
105 info[route.method] = pathInfo
106
107 return info
108 }
109
110 buildPaths(routes, isAdmin = false) {
111 const routePaths = {}
112
113 _.forEach(routes, route => {
114 // if (!route.path) continue
115 // if (!["get", "post","put","delete"].includes(route.method)) continue
116 const routePath = ((routePath, params) => {
117 _.forEach(params, param => {
118 routePath = routePath.replace( "(\\d+)", "" )
119 routePath = routePath.replace( ":" + param.name, "{" + param.name + "}" )
120 })
121
122 return routePath
123 })(route.routePath, route.params)
124
125 debug("routePath", routePath)
126
127 let pathInfo = this.pathInfo(route, routePaths[routePath] || {}, isAdmin)
128 if (pathInfo) {
129 routePaths[routePath] = pathInfo
130 }
131 })
132
133 return routePaths
134 }
135
136 getSchemaInfos(model) {
137 const modelObjects = []
138 const schemaLength = model.design._schemas.length
139 if (schemaLength > 0) {
140 _.forEach(model.design._schemas, schema => {
141
142 const properties = {}
143 _.map(schema.attributes, attribute => {
144 if (attribute.length) {
145 properties[attribute.key] = {
146 type: `${attribute.type}(${attribute.length})`
147 }
148 }
149 else {
150 properties[attribute.key] = {
151 type: `${attribute.type}`
152 }
153 }
154 })
155
156 const modelObject = {
157 name: schema.default === true ? model.name : `${model.name}.${schema.name}`,
158 description: schema.description || "",
159 properties: properties
160 }
161
162 modelObjects.push(modelObject)
163 })
164 }
165 return modelObjects
166 }
167
168 buildModels(models) {
169 const modelObjects = {}
170
171 _.forEach(models, model => {
172 let schemaInfos = this.getSchemaInfos(model)
173
174 _.forEach(schemaInfos, schemaInfo => {
175 modelObjects[schemaInfo.name] = schemaInfo
176 })
177 })
178
179 return modelObjects
180 }
181
182 getErrors() {
183 let constaints = this._context.constants["ERROR"] || {}
184 let errors = {}
185 for (var key in constaints) {
186 let constaint = constaints[key]
187 let errorComponents = constaint.split(":")
188 let error = {
189 type: errorComponents[1]
190 }
191
192 errors[errorComponents[0]] = error
193 }
194 return errors
195 }
196
197 buildJSON(options = {}, isAdmin = false) {
198 const { routes, models } = options
199 const routePaths = this.buildPaths(routes, isAdmin)
200 const host = this._config.host ?
201 this._config.host :
202 ip.address() + ":" + this._context.port
203
204 const modelObjects = this.buildModels(models, isAdmin)
205 const errors = this.getErrors()
206
207 const json = {
208 swagger: "2.0",
209 info: {
210 title: this._config.title || "API",
211 description: "Auto created swagger api document by motif",
212 version: this._config.version || "0.0.0"
213 },
214 securityDefinitions: {
215 APIKeyHeader: {
216 type: "apiKey",
217 in: "header",
218 name: "x-access-token"
219 }
220 },
221 definitions: {
222 "BasicResult": {
223 type: "object",
224 properties: {
225 error: {
226 $ref: "#/definitions/CommonError"
227 },
228 status: {
229 type: "string"
230 },
231 data: {
232 type: "json"
233 }
234 }
235 },
236 "CommonError": {
237 type: "object",
238 properties: errors
239 },
240 ...modelObjects
241 },
242 host: host,
243 basePath: "/",
244 schemes: this._config.schemes || ["http", "https"],
245 paths: routePaths
246 }
247
248 return json
249 }
250
251 jsonWriteAsync(path, json) {
252 return new Promise((resolve, reject) => {
253 jsonfile.writeFile(path, json, function (err) {
254 if (err) {
255 console.error(`json ${path} create fail !!`)
256 return reject(err)
257 }
258 // console.log(`json ${path} create success`)
259 resolve()
260 })
261 })
262 }
263}
264
265module.exports = Swagger