-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
6,483 additions
and
213 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,13 +7,10 @@ Then you may render it easily with [SwaggerUI](https://github.com/swagger-api/sw | |
|
||
# What is supported | ||
|
||
- customSchema in OAS v2 or v3 formats | ||
- convert _service_ to paths | ||
- convert _enum_, _message_ into components, paths will reference to the components schema | ||
- basic types mapping to JS type _number_, _string_, _boolean_ ( long types will be mapped to _string_) | ||
- recognize fields: | ||
- [OperationObject](https://swagger.io/specification/#operationObject).requestBody.\$proto | ||
Replace requestBody with a [Reference Object](https://swagger.io/specification/#referenceObject) | ||
- [OperationObject](https://swagger.io/specification/#operationObject).responses.\$proto | ||
Replace responses['200'] with a [Reference Object](https://swagger.io/specification/#referenceObject) | ||
|
||
# Install | ||
|
||
|
@@ -35,60 +32,23 @@ Example: | |
|
||
```javascript | ||
module.exports = { | ||
file: 'test.proto', | ||
// or multiple files | ||
files: ['test1.proto', 'test2.proto'], | ||
dist: 'apischema.json', | ||
formatServicePath: (path) => path.replace(/\./g, '/'), | ||
customSchema: { | ||
// Similar to openapi v3 format | ||
info: { | ||
title: 'API', | ||
version: '1.0.0', | ||
contact: { | ||
name: 'Jennie Ji', | ||
email: '[email protected]', | ||
url: 'jennieji.github.io', | ||
}, | ||
}, | ||
tags: [ | ||
{ | ||
name: 'test', | ||
description: '', | ||
}, | ||
], | ||
swagger: '2.0', | ||
paths: { | ||
'/api/test': { | ||
get: { | ||
requestBody: { | ||
$proto: 'GetDataRequest', // Tell me the protobuf message name | ||
}, | ||
responses: { | ||
$proto: 'GetDataResponse', // Tell me the protobuf message name | ||
'200': { | ||
schema: { | ||
$ref: 'GetDataResponse', // Tell me the protobuf message name | ||
}, | ||
}, | ||
}, | ||
params: [], | ||
}, | ||
// or customize | ||
post: { | ||
requestBody: { | ||
content: { | ||
'application/json': { | ||
schema: { | ||
$ref: '#/components/schemas/GetDataRequest' | ||
} | ||
} | ||
} | ||
}, | ||
responses: { | ||
'200': { | ||
content: { | ||
'application/json': { | ||
schema: { | ||
$ref: '#/components/schemas/GetDataResponse' | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
}, | ||
components: { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
const protobufjs = require('protobufjs'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const converter = require('swagger2openapi'); | ||
const processComponents = require('./processComponents'); | ||
const service2Paths = require('./service2Paths'); | ||
|
||
async function convert({ files, customSchema = {}, formatServicePath }) { | ||
const protobuf = new protobufjs.Root(); | ||
if (files) { | ||
files.forEach((file) => { | ||
protobuf.loadSync(file, { | ||
alternateCommentMode: true, | ||
}); | ||
}); | ||
} | ||
if (customSchema.swagger) { | ||
customSchema = converter.convertObj(swagger); | ||
} | ||
let { paths, components } = customSchema; | ||
|
||
const flattenedTypes = flattenPath(protobuf.nested); | ||
Object.values(flattenedTypes).forEach((def) => { | ||
if (/^Service /.test(def.toString())) { | ||
paths = { | ||
...service2Paths(def, formatServicePath), | ||
...(paths || {}), | ||
}; | ||
} | ||
}); | ||
|
||
return { | ||
openapi: '3.0.3', | ||
info: { | ||
title: protobuf.name || path.basename(files[0], '.proto'), | ||
description: protobuf.comment, | ||
verion: '1', | ||
}, | ||
paths, | ||
components: { | ||
schemas: Object.assign( | ||
processComponents(flattenedTypes), | ||
(components && components.schemas) || {} | ||
), | ||
...(components || {}), | ||
}, | ||
...customSchema, | ||
}; | ||
} | ||
|
||
function flattenPath(protobuf) { | ||
let flattened = {}; | ||
Object.keys(protobuf).forEach((key) => { | ||
const val = protobuf[key]; | ||
if (val.nested) { | ||
Object.entries(flattenPath(val.nested)).forEach(([type, def]) => { | ||
flattened[`${key}.${type}`] = def; | ||
}); | ||
} else { | ||
flattened[key] = val; | ||
} | ||
}); | ||
return flattened; | ||
} | ||
|
||
module.exports = convert; |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
const mapType = require('./mapType'); | ||
const bakeRef = require('./bakeRef'); | ||
|
||
function field2JSON({ | ||
type, | ||
comment: description, | ||
repeated, | ||
typeDefault, | ||
root, | ||
}) { | ||
const schema = {}; | ||
const mappedType = mapType(type); | ||
if (mappedType) { | ||
schema.type = mappedType; | ||
schema.format = ''; | ||
schema.default = typeDefault; | ||
} else { | ||
const { lookupEnum, lookupType } = root; | ||
try { | ||
const enumDef = lookupEnum(type); | ||
schema.enum = Object.values(enumDef); | ||
} catch (e) { | ||
schema.$ref = bakeRef(type); | ||
} | ||
} | ||
|
||
return repeated | ||
? { | ||
type: 'array', | ||
items: schema, | ||
description, | ||
} | ||
: schema; | ||
} | ||
|
||
module.exports = field2JSON; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = require('./convert'); |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
const mapType = require('./mapType'); | ||
const bakeRef = require('./bakeRef'); | ||
|
||
function type2Parameters(type, paramIn = 'query') { | ||
return type.fieldsArray.map((field) => { | ||
const { name, comment, required, typeDefault, repeated } = field; | ||
let ret = { | ||
name, | ||
description: comment, | ||
paramIn, | ||
required, | ||
}; | ||
if (field.type === 'string') { | ||
ret = { | ||
...ret, | ||
...field2JSON(type), | ||
}; | ||
} else { | ||
ret = { | ||
...ret, | ||
type: 'string', | ||
}; | ||
} | ||
return ret; | ||
}); | ||
} | ||
|
||
function service2Paths(def, formatServicePath) { | ||
const schemas = {}; | ||
Object.values(def.methods).forEach((method) => { | ||
const options = method.options || {}; | ||
let path = options.path || `/${def.fullName.slice(1)}/${method.name}`; | ||
if (formatServicePath) { | ||
path = formatServicePath(path); | ||
} | ||
let parameters = []; | ||
let requestBody = {}; | ||
try { | ||
const requestType = def.lookupType(method.requestType); | ||
if (options.method === 'get') { | ||
parameters = type2Parameters(requestType); | ||
} else { | ||
requestBody = { | ||
description: requestType.comment, | ||
content: { | ||
'application/json': { | ||
schema: { | ||
$ref: bakeRef(requestType.fullName.slice(1)), | ||
}, | ||
}, | ||
}, | ||
}; | ||
} | ||
} catch (e) {} | ||
schemas[path] = { | ||
[options.method || 'post']: { | ||
operationId: method.fullName.slice(1), | ||
description: method.comment, | ||
parameters, | ||
requestBody, | ||
responses: { | ||
'200': { | ||
content: { | ||
'application/json': { | ||
schema: { | ||
$ref: bakeRef(method.responseType), | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}; | ||
}); | ||
return schemas; | ||
} | ||
module.exports = service2Paths; |
Oops, something went wrong.