1 | "use strict"
|
2 |
|
3 | const _ = require("lodash")
|
4 | const jsonfile = require("jsonfile")
|
5 | const shell = require("shelljs")
|
6 | const ip = require("ip")
|
7 | const fs = require("fs")
|
8 |
|
9 | const debug = require("debug")("motif:swagger")
|
10 |
|
11 | class 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 |
|
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 |
|
115 |
|
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 |
|
259 | resolve()
|
260 | })
|
261 | })
|
262 | }
|
263 | }
|
264 |
|
265 | module.exports = Swagger
|